mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2026-03-02 22:57:00 +00:00
Quickbooks validation tests
This commit is contained in:
@@ -1 +1 @@
|
||||
5.12.49
|
||||
5.12.50
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataMapper;
|
||||
|
||||
/**
|
||||
* QuickbooksPushEvents.
|
||||
*
|
||||
* Stores push event configuration for QuickBooks integration.
|
||||
* This class provides a clean separation of push event settings.
|
||||
*/
|
||||
class QuickbooksPushEvents
|
||||
{
|
||||
/**
|
||||
* Push when a new client is created.
|
||||
*/
|
||||
public bool $push_on_new_client = false;
|
||||
|
||||
/**
|
||||
* Push when an existing client is updated.
|
||||
*/
|
||||
public bool $push_on_updated_client = false;
|
||||
|
||||
/**
|
||||
* Push when an invoice status matches one of these values.
|
||||
*
|
||||
* Valid values: 'draft', 'sent', 'paid', 'deleted'
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
public array $push_invoice_statuses = [];
|
||||
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
$this->push_on_new_client = $attributes['push_on_new_client'] ?? false;
|
||||
$this->push_on_updated_client = $attributes['push_on_updated_client'] ?? false;
|
||||
$this->push_invoice_statuses = $attributes['push_invoice_statuses'] ?? [];
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'push_on_new_client' => $this->push_on_new_client,
|
||||
'push_on_updated_client' => $this->push_on_updated_client,
|
||||
'push_invoice_statuses' => $this->push_invoice_statuses,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,8 @@ class QuickbooksSettings implements Castable
|
||||
|
||||
public string $baseURL;
|
||||
|
||||
public string $companyName;
|
||||
|
||||
public QuickbooksSync $settings;
|
||||
|
||||
public function __construct(array $attributes = [])
|
||||
@@ -42,6 +44,7 @@ class QuickbooksSettings implements Castable
|
||||
$this->accessTokenExpiresAt = $attributes['accessTokenExpiresAt'] ?? 0;
|
||||
$this->refreshTokenExpiresAt = $attributes['refreshTokenExpiresAt'] ?? 0;
|
||||
$this->baseURL = $attributes['baseURL'] ?? '';
|
||||
$this->companyName = $attributes['companyName'] ?? '';
|
||||
$this->settings = new QuickbooksSync($attributes['settings'] ?? []);
|
||||
}
|
||||
|
||||
@@ -64,6 +67,7 @@ class QuickbooksSettings implements Castable
|
||||
'accessTokenExpiresAt' => $this->accessTokenExpiresAt,
|
||||
'refreshTokenExpiresAt' => $this->refreshTokenExpiresAt,
|
||||
'baseURL' => $this->baseURL,
|
||||
'companyName' => $this->companyName,
|
||||
'settings' => $this->settings->toArray(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -39,8 +39,6 @@ class QuickbooksSync
|
||||
|
||||
public string $default_expense_account = '';
|
||||
|
||||
public QuickbooksPushEvents $push_events;
|
||||
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
$this->client = new QuickbooksSyncMap($attributes['client'] ?? []);
|
||||
@@ -54,7 +52,6 @@ class QuickbooksSync
|
||||
$this->expense = new QuickbooksSyncMap($attributes['expense'] ?? []);
|
||||
$this->default_income_account = $attributes['default_income_account'] ?? '';
|
||||
$this->default_expense_account = $attributes['default_expense_account'] ?? '';
|
||||
$this->push_events = new QuickbooksPushEvents($attributes['push_events'] ?? []);
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
@@ -71,7 +68,6 @@ class QuickbooksSync
|
||||
'expense' => $this->expense->toArray(),
|
||||
'default_income_account' => $this->default_income_account,
|
||||
'default_expense_account' => $this->default_expense_account,
|
||||
'push_events' => $this->push_events->toArray(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@ use App\Enum\SyncDirection;
|
||||
*/
|
||||
class QuickbooksSyncMap
|
||||
{
|
||||
public SyncDirection $direction = SyncDirection::BIDIRECTIONAL;
|
||||
public SyncDirection $direction = SyncDirection::NONE;
|
||||
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
$this->direction = isset($attributes['direction'])
|
||||
? SyncDirection::from($attributes['direction'])
|
||||
: SyncDirection::BIDIRECTIONAL;
|
||||
: SyncDirection::NONE;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
|
||||
@@ -33,8 +33,6 @@ class ImportQuickbooksController extends BaseController
|
||||
|
||||
$authorizationUrl = $qb->sdk()->getAuthorizationUrl();
|
||||
|
||||
nlog($authorizationUrl);
|
||||
|
||||
$state = $qb->sdk()->getState();
|
||||
|
||||
Cache::put($state, $token, 190);
|
||||
|
||||
63
app/Http/Controllers/QuickbooksController.php
Normal file
63
app/Http/Controllers/QuickbooksController.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Http\Requests\Quickbooks\ConfigQuickbooksRequest;
|
||||
use App\Http\Requests\Quickbooks\DisconnectQuickbooksRequest;
|
||||
use App\Http\Requests\Quickbooks\SyncQuickbooksRequest;
|
||||
|
||||
class QuickbooksController extends BaseController
|
||||
{
|
||||
|
||||
public function sync(SyncQuickbooksRequest $request)
|
||||
{
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
public function configuration(ConfigQuickbooksRequest $request)
|
||||
{
|
||||
|
||||
$user = auth()->user();
|
||||
$company = $user->company();
|
||||
|
||||
$quickbooks = $company->quickbooks;
|
||||
$quickbooks->settings->client->direction = $request->clients ? SyncDirection::PUSH : SyncDirection::NONE;
|
||||
$quickbooks->settings->vendor->direction = $request->vendors ? SyncDirection::PUSH : SyncDirection::NONE;
|
||||
$quickbooks->settings->product->direction = $request->products ? SyncDirection::PUSH : SyncDirection::NONE;
|
||||
$quickbooks->settings->invoice->direction = $request->invoices ? SyncDirection::PUSH : SyncDirection::NONE;
|
||||
$quickbooks->settings->quote->direction = $request->quotes ? SyncDirection::PUSH : SyncDirection::NONE;
|
||||
$quickbooks->settings->payment->direction = $request->payments ? SyncDirection::PUSH : SyncDirection::NONE;
|
||||
$company->quickbooks = $quickbooks;
|
||||
$company->save();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
public function disconnect(DisconnectQuickbooksRequest $request)
|
||||
{
|
||||
|
||||
$user = auth()->user();
|
||||
$company = $user->company();
|
||||
|
||||
$qb = new QuickbooksService($company);
|
||||
$qb->sdk()->revokeAccessToken();
|
||||
|
||||
$company->quickbooks = null;
|
||||
$company->save();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
}
|
||||
49
app/Http/Requests/Quickbooks/ConfigQuickbooksRequest.php
Normal file
49
app/Http/Requests/Quickbooks/ConfigQuickbooksRequest.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Quickbooks;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class ConfigQuickbooksRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'invoices' => 'required|boolean|bail',
|
||||
'quotes' => 'required|boolean|bail',
|
||||
'payments' => 'required|boolean|bail',
|
||||
'products' => 'required|boolean|bail',
|
||||
'vendors' => 'required|boolean|bail',
|
||||
'clients' => 'required|boolean|bail',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
44
app/Http/Requests/Quickbooks/DisconnectQuickbooksRequest.php
Normal file
44
app/Http/Requests/Quickbooks/DisconnectQuickbooksRequest.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Quickbooks;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class DisconnectQuickbooksRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
97
app/Http/Requests/Quickbooks/SyncQuickbooksRequest.php
Normal file
97
app/Http/Requests/Quickbooks/SyncQuickbooksRequest.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Quickbooks;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class SyncQuickbooksRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'clients' => [
|
||||
'present_with:invoices,quotes,payments',
|
||||
'nullable',
|
||||
function ($attribute, $value, $fail) {
|
||||
// If value is provided and not empty, validate it
|
||||
if ($value !== null && $value !== '' && !in_array($value, ['email', 'name'])) {
|
||||
$fail('The ' . $attribute . ' must be one of: email, name.');
|
||||
}
|
||||
},
|
||||
],
|
||||
'products' => ['sometimes', 'nullable', function ($attribute, $value, $fail) {
|
||||
if ($value !== null && $value !== '' && $value !== 'product_key') {
|
||||
$fail('The ' . $attribute . ' must be product_key.');
|
||||
}
|
||||
}],
|
||||
'invoices' => ['sometimes', 'nullable', function ($attribute, $value, $fail) {
|
||||
if ($value !== null && $value !== '' && $value !== 'number') {
|
||||
$fail('The ' . $attribute . ' must be number.');
|
||||
}
|
||||
}],
|
||||
'quotes' => ['sometimes', 'nullable', function ($attribute, $value, $fail) {
|
||||
if ($value !== null && $value !== '' && $value !== 'number') {
|
||||
$fail('The ' . $attribute . ' must be number.');
|
||||
}
|
||||
}],
|
||||
'payments' => 'sometimes|nullable',
|
||||
'vendors' => ['sometimes', 'nullable', function ($attribute, $value, $fail) {
|
||||
if ($value !== null && $value !== '' && !in_array($value, ['email', 'name'])) {
|
||||
$fail('The ' . $attribute . ' must be one of: email, name.');
|
||||
}
|
||||
}],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
* Convert empty strings to null for nullable fields.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
// Convert empty strings to null for nullable fields
|
||||
$nullableFields = ['clients', 'products', 'invoices', 'quotes', 'payments', 'vendors'];
|
||||
|
||||
foreach ($nullableFields as $field) {
|
||||
if (isset($input[$field]) && $input[$field] === '') {
|
||||
$input[$field] = null;
|
||||
}
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1089,7 +1089,7 @@ class Company extends BaseModel
|
||||
|
||||
// Cache the detailed check for this request lifecycle
|
||||
// This prevents re-checking if called multiple times in the same request
|
||||
return once(function () use ($entity, $action, $status) {
|
||||
return once(function () use ($entity) {
|
||||
// Check if QuickBooks is actually configured (has token)
|
||||
if (!$this->quickbooks->isConfigured()) {
|
||||
return false;
|
||||
@@ -1104,26 +1104,7 @@ class Company extends BaseModel
|
||||
$direction = $entitySettings->direction->value;
|
||||
|
||||
// Check if sync direction allows push
|
||||
if ($direction !== 'push' && $direction !== 'bidirectional') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get push events from settings
|
||||
$pushEvents = $this->quickbooks->settings->push_events;
|
||||
|
||||
// Check action-specific settings from QuickbooksPushEvents
|
||||
return match($action) {
|
||||
'create' => match($entity) {
|
||||
'client' => $pushEvents->push_on_new_client ?? false,
|
||||
default => false, // Other entities can be added here
|
||||
},
|
||||
'update' => match($entity) {
|
||||
'client' => $pushEvents->push_on_updated_client ?? false,
|
||||
default => false, // Other entities can be added here
|
||||
},
|
||||
'status' => $status && in_array($status, $pushEvents->push_invoice_statuses ?? []),
|
||||
default => false,
|
||||
};
|
||||
return $direction === 'push' || $direction === 'bidirectional';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => env('APP_VERSION', '5.12.49'),
|
||||
'app_tag' => env('APP_TAG', '5.12.49'),
|
||||
'app_version' => env('APP_VERSION', '5.12.50'),
|
||||
'app_tag' => env('APP_TAG', '5.12.50'),
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', false),
|
||||
|
||||
@@ -336,6 +336,9 @@ Route::group(['middleware' => ['throttle:api', 'token_auth', 'valid_json','local
|
||||
Route::get('quote/{invitation_key}/download', [QuoteController::class, 'downloadPdf'])->name('quotes.downloadPdf');
|
||||
Route::get('quote/{invitation_key}/download_e_quote', [QuoteController::class, 'downloadEQuote'])->name('quotes.downloadEQuote');
|
||||
|
||||
Route::post('quickbooks/sync', [ImportQuickbooksController::class, 'sync'])->name('quickbooks.sync');
|
||||
Route::post('quickbooks/configuration', [ImportQuickbooksController::class, 'configuration'])->name('quickbooks.configuration');
|
||||
|
||||
Route::resource('recurring_expenses', RecurringExpenseController::class);
|
||||
Route::post('recurring_expenses/bulk', [RecurringExpenseController::class, 'bulk'])->name('recurring_expenses.bulk');
|
||||
Route::put('recurring_expenses/{recurring_expense}/upload', [RecurringExpenseController::class, 'upload']);
|
||||
|
||||
@@ -0,0 +1,467 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace Tests\Feature\Quickbooks\Validation;
|
||||
|
||||
use Tests\TestCase;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\Http\Requests\Quickbooks\SyncQuickbooksRequest;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Tests\MockAccountData;
|
||||
|
||||
class SyncQuickbooksRequestTest extends TestCase
|
||||
{
|
||||
use MockAccountData;
|
||||
|
||||
protected SyncQuickbooksRequest $request;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->request = new SyncQuickbooksRequest();
|
||||
|
||||
$this->withoutMiddleware(
|
||||
ThrottleRequests::class
|
||||
);
|
||||
|
||||
$this->makeTestData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that clients can be provided on its own (without invoices/quotes/payments)
|
||||
*/
|
||||
public function testClientsCanBeProvidedAlone(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => 'email',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Clients should be valid when provided alone');
|
||||
}
|
||||
|
||||
|
||||
public function testClientsCanBeProvidedAloneWithEmptyString(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => '',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Clients should be valid when provided alone');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that clients can be null/empty when provided alone
|
||||
*/
|
||||
public function testClientsCanBeNullWhenAlone(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => null,
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Clients should be valid when null and no invoices/quotes/payments');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that clients can be empty string when provided alone
|
||||
*/
|
||||
public function testClientsCanBeEmptyStringWhenAlone(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => '',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Clients should be valid when empty string and no invoices/quotes/payments');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that clients is required when invoices is present
|
||||
*/
|
||||
public function testClientsIsRequiredWhenInvoicesPresent(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'invoices' => 'number',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertFalse($validator->passes(), 'Clients should be required when invoices is present');
|
||||
$this->assertArrayHasKey('clients', $validator->errors()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that clients is required when quotes is present
|
||||
*/
|
||||
public function testClientsIsRequiredWhenQuotesPresent(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'quotes' => 'number',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertFalse($validator->passes(), 'Clients should be required when quotes is present');
|
||||
$this->assertArrayHasKey('clients', $validator->errors()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that clients is required when payments is present
|
||||
*/
|
||||
public function testClientsIsRequiredWhenPaymentsPresent(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'payments' => true,
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertFalse($validator->passes(), 'Clients should be required when payments is present');
|
||||
$this->assertArrayHasKey('clients', $validator->errors()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that clients is required when multiple dependent fields are present
|
||||
*/
|
||||
public function testClientsIsRequiredWhenMultipleDependentFieldsPresent(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'invoices' => 'number',
|
||||
'quotes' => 'number',
|
||||
'payments' => true,
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertFalse($validator->passes(), 'Clients should be required when invoices, quotes, and payments are present');
|
||||
$this->assertArrayHasKey('clients', $validator->errors()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that clients with valid value 'email' passes when invoices is present
|
||||
*/
|
||||
public function testClientsWithEmailPassesWhenInvoicesPresent(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => 'email',
|
||||
'invoices' => 'number',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Clients with email should be valid when invoices is present');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that clients with valid value 'name' passes when invoices is present
|
||||
*/
|
||||
public function testClientsWithNamePassesWhenInvoicesPresent(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => 'name',
|
||||
'invoices' => 'number',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Clients with name should be valid when invoices is present');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that clients with empty string passes when invoices is present (nullable)
|
||||
*/
|
||||
public function testClientsWithEmptyStringPassesWhenInvoicesPresent(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => '',
|
||||
'invoices' => 'number',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Clients with empty string should be valid when invoices is present (nullable)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that clients with invalid value fails
|
||||
*/
|
||||
public function testClientsWithInvalidValueFails(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => 'invalid_value',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertFalse($validator->passes(), 'Clients with invalid value should fail');
|
||||
$this->assertArrayHasKey('clients', $validator->errors()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that products with valid value passes
|
||||
*/
|
||||
public function testProductsWithValidValuePasses(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'products' => 'product_key',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Products with product_key should be valid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that products with invalid value fails
|
||||
*/
|
||||
public function testProductsWithInvalidValueFails(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'products' => 'invalid_value',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertFalse($validator->passes(), 'Products with invalid value should fail');
|
||||
$this->assertArrayHasKey('products', $validator->errors()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that invoices with valid value passes
|
||||
*/
|
||||
public function testInvoicesWithValidValuePasses(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => 'email',
|
||||
'invoices' => 'number',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Invoices with number should be valid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that invoices with invalid value fails
|
||||
*/
|
||||
public function testInvoicesWithInvalidValueFails(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => 'email',
|
||||
'invoices' => 'invalid_value',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertFalse($validator->passes(), 'Invoices with invalid value should fail');
|
||||
$this->assertArrayHasKey('invoices', $validator->errors()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that quotes with valid value passes
|
||||
*/
|
||||
public function testQuotesWithValidValuePasses(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => 'email',
|
||||
'quotes' => 'number',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Quotes with number should be valid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that quotes with invalid value fails
|
||||
*/
|
||||
public function testQuotesWithInvalidValueFails(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => 'email',
|
||||
'quotes' => 'invalid_value',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertFalse($validator->passes(), 'Quotes with invalid value should fail');
|
||||
$this->assertArrayHasKey('quotes', $validator->errors()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that vendors with valid value passes
|
||||
*/
|
||||
public function testVendorsWithValidValuePasses(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'vendors' => 'email',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Vendors with email should be valid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that vendors with name value passes
|
||||
*/
|
||||
public function testVendorsWithNameValuePasses(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'vendors' => 'name',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Vendors with name should be valid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that vendors with invalid value fails
|
||||
*/
|
||||
public function testVendorsWithInvalidValueFails(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'vendors' => 'invalid_value',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertFalse($validator->passes(), 'Vendors with invalid value should fail');
|
||||
$this->assertArrayHasKey('vendors', $validator->errors()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that all fields can be provided together with valid values
|
||||
*/
|
||||
public function testAllFieldsWithValidValuesPasses(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => 'email',
|
||||
'products' => 'product_key',
|
||||
'invoices' => 'number',
|
||||
'quotes' => 'number',
|
||||
'payments' => true,
|
||||
'vendors' => 'name',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'All fields with valid values should pass');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that empty request passes (all fields are optional)
|
||||
*/
|
||||
public function testEmptyRequestPasses(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Empty request should pass (all fields are optional)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that payments can be any value (no validation on payments field itself)
|
||||
*/
|
||||
public function testPaymentsCanBeAnyValue(): void
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$data = [
|
||||
'clients' => 'email',
|
||||
'payments' => 'any_value_here',
|
||||
];
|
||||
|
||||
$this->request->initialize($data);
|
||||
$validator = Validator::make($data, $this->request->rules());
|
||||
|
||||
$this->assertTrue($validator->passes(), 'Payments can be any value when clients is provided');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user