mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge pull request #9291 from turbo124/v5-develop
Fixes for openapi spec
This commit is contained in:
commit
a72058525e
@ -79,6 +79,7 @@ class OpenApiYaml extends Command
|
|||||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components.yaml'));
|
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components.yaml'));
|
||||||
|
|
||||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components/examples.yaml'));
|
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components/examples.yaml'));
|
||||||
|
|
||||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components/responses.yaml'));
|
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components/responses.yaml'));
|
||||||
|
|
||||||
$directory = new DirectoryIterator($path . '/components/responses/');
|
$directory = new DirectoryIterator($path . '/components/responses/');
|
||||||
|
@ -225,22 +225,6 @@ class ClientExport extends BaseExport
|
|||||||
$entity['client.assigned_user'] = $client->assigned_user ? $client->user->present()->name() : '';
|
$entity['client.assigned_user'] = $client->assigned_user ? $client->user->present()->name() : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (in_array('client.country_id', $this->input['report_keys'])) {
|
|
||||||
// $entity['client.country_id'] = $client->country ? ctrans("texts.country_{$client->country->name}") : '';
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (in_array('client.shipping_country_id', $this->input['report_keys'])) {
|
|
||||||
// $entity['client.shipping_country_id'] = $client->shipping_country ? ctrans("texts.country_{$client->shipping_country->name}") : '';
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (in_array('client.currency_id', $this->input['report_keys'])) {
|
|
||||||
// $entity['client.currency_id'] = $client->currency() ? $client->currency()->code : $client->company->currency()->code;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (in_array('client.industry_id', $this->input['report_keys'])) {
|
|
||||||
// $entity['industry_id'] = $client->industry ? ctrans("texts.industry_{$client->industry->name}") : '';
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (in_array('client.classification', $this->input['report_keys']) && isset($client->classification)) {
|
if (in_array('client.classification', $this->input['report_keys']) && isset($client->classification)) {
|
||||||
$entity['client.classification'] = ctrans("texts.{$client->classification}") ?? '';
|
$entity['client.classification'] = ctrans("texts.{$client->classification}") ?? '';
|
||||||
}
|
}
|
||||||
|
@ -165,6 +165,11 @@ class ClientFilters extends QueryFilters
|
|||||||
|
|
||||||
$dir = ($sort_col[1] == 'asc') ? 'asc' : 'desc';
|
$dir = ($sort_col[1] == 'asc') ? 'asc' : 'desc';
|
||||||
|
|
||||||
|
if($sort_col[0] == 'number')
|
||||||
|
{
|
||||||
|
return $this->builder->orderByRaw('ABS(number) ' . $dir);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->builder->orderBy($sort_col[0], $dir);
|
return $this->builder->orderBy($sort_col[0], $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,6 +146,11 @@ class CreditFilters extends QueryFilters
|
|||||||
->whereColumn('clients.id', 'credits.client_id'), $dir);
|
->whereColumn('clients.id', 'credits.client_id'), $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if($sort_col[0] == 'number') {
|
||||||
|
return $this->builder->orderByRaw('ABS(number) ' . $dir);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->builder->orderBy($sort_col[0], $dir);
|
return $this->builder->orderBy($sort_col[0], $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +172,8 @@ class ExpenseFilters extends QueryFilters
|
|||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$dir = ($sort_col[1] == 'asc') ? 'asc' : 'desc';
|
||||||
|
|
||||||
if ($sort_col[0] == 'client_id' && in_array($sort_col[1], ['asc', 'desc'])) {
|
if ($sort_col[0] == 'client_id' && in_array($sort_col[1], ['asc', 'desc'])) {
|
||||||
return $this->builder
|
return $this->builder
|
||||||
->orderByRaw('ISNULL(client_id), client_id '. $sort_col[1])
|
->orderByRaw('ISNULL(client_id), client_id '. $sort_col[1])
|
||||||
@ -194,6 +196,10 @@ class ExpenseFilters extends QueryFilters
|
|||||||
->whereColumn('expense_categories.id', 'expenses.category_id'), $sort_col[1]);
|
->whereColumn('expense_categories.id', 'expenses.category_id'), $sort_col[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($sort_col[0] == 'number') {
|
||||||
|
return $this->builder->orderByRaw('ABS(number) ' . $dir);
|
||||||
|
}
|
||||||
|
|
||||||
if (is_array($sort_col) && in_array($sort_col[1], ['asc', 'desc']) && in_array($sort_col[0], ['public_notes', 'date', 'id_number', 'custom_value1', 'custom_value2', 'custom_value3', 'custom_value4'])) {
|
if (is_array($sort_col) && in_array($sort_col[1], ['asc', 'desc']) && in_array($sort_col[0], ['public_notes', 'date', 'id_number', 'custom_value1', 'custom_value2', 'custom_value3', 'custom_value4'])) {
|
||||||
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
||||||
}
|
}
|
||||||
|
@ -318,11 +318,16 @@ class InvoiceFilters extends QueryFilters
|
|||||||
|
|
||||||
if ($sort_col[0] == 'client_id') {
|
if ($sort_col[0] == 'client_id') {
|
||||||
|
|
||||||
return $this->builder->orderBy(\App\Models\Client::select('name')
|
return $this->builder->orderBy(\App\Models\Client::select ('name')
|
||||||
->whereColumn('clients.id', 'invoices.client_id'), $dir);
|
->whereColumn('clients.id', 'invoices.client_id'), $dir);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($sort_col[0] == 'number')
|
||||||
|
{
|
||||||
|
return $this->builder->orderByRaw('ABS(number) ' . $dir);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->builder->orderBy($sort_col[0], $dir);
|
return $this->builder->orderBy($sort_col[0], $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,14 +167,18 @@ class PaymentFilters extends QueryFilters
|
|||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$dir = ($sort_col[1] == 'asc') ? 'asc' : 'desc';
|
||||||
|
|
||||||
if ($sort_col[0] == 'client_id') {
|
if ($sort_col[0] == 'client_id') {
|
||||||
return $this->builder->orderBy(\App\Models\Client::select('name')
|
return $this->builder->orderBy(\App\Models\Client::select('name')
|
||||||
->whereColumn('clients.id', 'payments.client_id'), $sort_col[1]);
|
->whereColumn('clients.id', 'payments.client_id'), $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($sort_col[0] == 'number') {
|
||||||
|
return $this->builder->orderByRaw('ABS(number) ' . $dir);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
return $this->builder->orderBy($sort_col[0], $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function date_range(string $date_range = ''): Builder
|
public function date_range(string $date_range = ''): Builder
|
||||||
|
@ -59,21 +59,24 @@ class ProjectFilters extends QueryFilters
|
|||||||
public function sort(string $sort = ''): Builder
|
public function sort(string $sort = ''): Builder
|
||||||
{
|
{
|
||||||
$sort_col = explode('|', $sort);
|
$sort_col = explode('|', $sort);
|
||||||
|
|
||||||
if ($sort_col[0] == 'client_id') {
|
|
||||||
return $this->builder->orderBy(\App\Models\Client::select('name')
|
|
||||||
->whereColumn('clients.id', 'projects.client_id'), $sort_col[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_array($sort_col) || count($sort_col) != 2) {
|
if (!is_array($sort_col) || count($sort_col) != 2) {
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_array($sort_col) && in_array($sort_col[1], ['asc','desc'])) {
|
$dir = ($sort_col[1] == 'asc') ? 'asc' : 'desc';
|
||||||
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
|
||||||
|
if ($sort_col[0] == 'client_id') {
|
||||||
|
return $this->builder->orderBy(\App\Models\Client::select('name')
|
||||||
|
->whereColumn('clients.id', 'projects.client_id'), $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->builder;
|
if($sort_col[0] == 'number') {
|
||||||
|
return $this->builder->orderByRaw('ABS(number) ' . $dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->builder->orderBy($sort_col[0], $dir);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -130,6 +130,10 @@ class PurchaseOrderFilters extends QueryFilters
|
|||||||
->whereColumn('vendors.id', 'purchase_orders.vendor_id'), $dir);
|
->whereColumn('vendors.id', 'purchase_orders.vendor_id'), $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($sort_col[0] == 'number') {
|
||||||
|
return $this->builder->orderByRaw('ABS(number) ' . $dir);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->builder->orderBy($sort_col[0], $dir);
|
return $this->builder->orderBy($sort_col[0], $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +155,10 @@ class QuoteFilters extends QueryFilters
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($sort_col[0] == 'number') {
|
||||||
|
return $this->builder->orderByRaw('ABS(number) ' . $dir);
|
||||||
|
}
|
||||||
|
|
||||||
if ($sort_col[0] == 'valid_until') {
|
if ($sort_col[0] == 'valid_until') {
|
||||||
$sort_col[0] = 'due_date';
|
$sort_col[0] = 'due_date';
|
||||||
}
|
}
|
||||||
|
@ -140,6 +140,8 @@ class RecurringExpenseFilters extends QueryFilters
|
|||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$dir = ($sort_col[1] == 'asc') ? 'asc' : 'desc';
|
||||||
|
|
||||||
if ($sort_col[0] == 'client_id' && in_array($sort_col[1], ['asc', 'desc'])) {
|
if ($sort_col[0] == 'client_id' && in_array($sort_col[1], ['asc', 'desc'])) {
|
||||||
return $this->builder
|
return $this->builder
|
||||||
->orderByRaw('ISNULL(client_id), client_id '. $sort_col[1])
|
->orderByRaw('ISNULL(client_id), client_id '. $sort_col[1])
|
||||||
@ -162,6 +164,10 @@ class RecurringExpenseFilters extends QueryFilters
|
|||||||
->whereColumn('expense_categories.id', 'expenses.category_id'), $sort_col[1]);
|
->whereColumn('expense_categories.id', 'expenses.category_id'), $sort_col[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($sort_col[0] == 'number') {
|
||||||
|
return $this->builder->orderByRaw('ABS(number) ' . $dir);
|
||||||
|
}
|
||||||
|
|
||||||
if (is_array($sort_col) && in_array($sort_col[1], ['asc', 'desc']) && in_array($sort_col[0], ['public_notes', 'date', 'id_number', 'custom_value1', 'custom_value2', 'custom_value3', 'custom_value4'])) {
|
if (is_array($sort_col) && in_array($sort_col[1], ['asc', 'desc']) && in_array($sort_col[0], ['public_notes', 'date', 'id_number', 'custom_value1', 'custom_value2', 'custom_value3', 'custom_value4'])) {
|
||||||
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,10 @@ class TaskFilters extends QueryFilters
|
|||||||
->whereColumn('users.id', 'tasks.user_id'), $dir);
|
->whereColumn('users.id', 'tasks.user_id'), $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($sort_col[0] == 'number') {
|
||||||
|
return $this->builder->orderByRaw('ABS(number) ' . $dir);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->builder->orderBy($sort_col[0], $dir);
|
return $this->builder->orderBy($sort_col[0], $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +71,10 @@ class VendorFilters extends QueryFilters
|
|||||||
|
|
||||||
$dir = ($sort_col[1] == 'asc') ? 'asc' : 'desc';
|
$dir = ($sort_col[1] == 'asc') ? 'asc' : 'desc';
|
||||||
|
|
||||||
|
if($sort_col[0] == 'number') {
|
||||||
|
return $this->builder->orderByRaw('ABS(number) ' . $dir);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->builder->orderBy($sort_col[0], $dir);
|
return $this->builder->orderBy($sort_col[0], $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ class StoreClientRequest extends Request
|
|||||||
|
|
||||||
$rules['number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)];
|
$rules['number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)];
|
||||||
$rules['id_number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)];
|
$rules['id_number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)];
|
||||||
$rules['classification'] = 'bail|sometimes|nullable|in:individual,business,partnership,trust,charity,government,other';
|
$rules['classification'] = 'bail|sometimes|nullable|in:individual,business,company,partnership,trust,charity,government,other';
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ class UpdateClientRequest extends Request
|
|||||||
$rules['size_id'] = 'integer|nullable';
|
$rules['size_id'] = 'integer|nullable';
|
||||||
$rules['country_id'] = 'integer|nullable';
|
$rules['country_id'] = 'integer|nullable';
|
||||||
$rules['shipping_country_id'] = 'integer|nullable';
|
$rules['shipping_country_id'] = 'integer|nullable';
|
||||||
$rules['classification'] = 'bail|sometimes|nullable|in:individual,business,partnership,trust,charity,government,other';
|
$rules['classification'] = 'bail|sometimes|nullable|in:individual,business,company,partnership,trust,charity,government,other';
|
||||||
|
|
||||||
if ($this->id_number) {
|
if ($this->id_number) {
|
||||||
$rules['id_number'] = Rule::unique('clients')->where('company_id', $user->company()->id)->ignore($this->client->id);
|
$rules['id_number'] = Rule::unique('clients')->where('company_id', $user->company()->id)->ignore($this->client->id);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Requests\ExpenseCategory;
|
namespace App\Http\Requests\ExpenseCategory;
|
||||||
|
|
||||||
|
use App\Models\Expense;
|
||||||
use App\Http\Requests\Request;
|
use App\Http\Requests\Request;
|
||||||
use App\Models\ExpenseCategory;
|
use App\Models\ExpenseCategory;
|
||||||
|
|
||||||
@ -23,14 +24,21 @@ class StoreExpenseCategoryRequest extends Request
|
|||||||
*/
|
*/
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
{
|
{
|
||||||
return auth()->user()->can('create', ExpenseCategory::class);
|
/** @var \App\Models\User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
return $user->can('create', ExpenseCategory::class) || $user->can('create', Expense::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/** @var \App\Models\User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
$rules = [];
|
$rules = [];
|
||||||
|
|
||||||
$rules['name'] = 'required|unique:expense_categories,name,null,null,company_id,'.auth()->user()->companyId();
|
$rules['name'] = 'required|unique:expense_categories,name,null,null,company_id,'.$user->companyId();
|
||||||
|
|
||||||
return $this->globalRules($rules);
|
return $this->globalRules($rules);
|
||||||
}
|
}
|
||||||
|
@ -26,16 +26,24 @@ class UpdateExpenseCategoryRequest extends Request
|
|||||||
*/
|
*/
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
{
|
{
|
||||||
return auth()->user()->can('edit', $this->expense_category);
|
|
||||||
|
/** @var \App\Models\User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
return $user->can('edit', $this->expense_category);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/** @var \App\Models\User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
$rules = [];
|
$rules = [];
|
||||||
|
|
||||||
if ($this->input('name')) {
|
if ($this->input('name')) {
|
||||||
// $rules['name'] = 'unique:expense_categories,name,'.$this->id.',id,company_id,'.$this->expense_category->company_id;
|
// $rules['name'] = 'unique:expense_categories,name,'.$this->id.',id,company_id,'.$this->expense_category->company_id;
|
||||||
$rules['name'] = Rule::unique('expense_categories')->where('company_id', auth()->user()->company()->id)->ignore($this->expense_category->id);
|
$rules['name'] = Rule::unique('expense_categories')->where('company_id', $user->company()->id)->ignore($this->expense_category->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
|
@ -198,6 +198,18 @@ class Request extends FormRequest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(isset($input['public_notes']))
|
||||||
|
$input['public_notes'] = str_replace("</sc","<-", $input['public_notes']);
|
||||||
|
|
||||||
|
if(isset($input['footer']))
|
||||||
|
$input['footer'] = str_replace("</sc", "<-", $input['footer']);
|
||||||
|
|
||||||
|
if(isset($input['terms']))
|
||||||
|
$input['terms'] = str_replace("</sc", "<-", $input['terms']);
|
||||||
|
|
||||||
|
if(isset($input['private_notes']))
|
||||||
|
$input['private_notes'] = str_replace("</sc", "<-", $input['private_notes']);
|
||||||
|
|
||||||
return $input;
|
return $input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ class StoreVendorRequest extends Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
$rules['language_id'] = 'bail|nullable|sometimes|exists:languages,id';
|
$rules['language_id'] = 'bail|nullable|sometimes|exists:languages,id';
|
||||||
$rules['classification'] = 'bail|sometimes|nullable|in:individual,company,partnership,trust,charity,government,other';
|
$rules['classification'] = 'bail|sometimes|nullable|in:individual,business,company,partnership,trust,charity,government,other';
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
@ -89,6 +89,10 @@ class StoreVendorRequest extends Request
|
|||||||
$input['currency_id'] = $user->company()->settings->currency_id;
|
$input['currency_id'] = $user->company()->settings->currency_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($input['name'])) {
|
||||||
|
$input['name'] = strip_tags($input['name']);
|
||||||
|
}
|
||||||
|
|
||||||
$input = $this->decodePrimaryKeys($input);
|
$input = $this->decodePrimaryKeys($input);
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
|
@ -74,7 +74,7 @@ class UpdateVendorRequest extends Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
$rules['language_id'] = 'bail|nullable|sometimes|exists:languages,id';
|
$rules['language_id'] = 'bail|nullable|sometimes|exists:languages,id';
|
||||||
$rules['classification'] = 'bail|sometimes|nullable|in:individual,company,partnership,trust,charity,government,other';
|
$rules['classification'] = 'bail|sometimes|nullable|in:individual,business,company,partnership,trust,charity,government,other';
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
@ -92,8 +92,8 @@ class UpdateVendorRequest extends Request
|
|||||||
{
|
{
|
||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
|
if (isset($input['name'])) {
|
||||||
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
|
$input['name'] = strip_tags($input['name']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('country_id', $input) && is_null($input['country_id'])) {
|
if (array_key_exists('country_id', $input) && is_null($input['country_id'])) {
|
||||||
|
@ -1239,7 +1239,7 @@ class PdfBuilder
|
|||||||
public function productTable(): array
|
public function productTable(): array
|
||||||
{
|
{
|
||||||
$product_items = collect($this->service->config->entity->line_items)->filter(function ($item) {
|
$product_items = collect($this->service->config->entity->line_items)->filter(function ($item) {
|
||||||
return $item->type_id == 1 || $item->type_id == 6 || $item->type_id == 5;
|
return $item->type_id == 1 || $item->type_id == 6 || $item->type_id == 5 || $item->type_id == 4;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (count($product_items) == 0) {
|
if (count($product_items) == 0) {
|
||||||
|
@ -441,7 +441,7 @@ class Design extends BaseDesign
|
|||||||
public function productTable(): array
|
public function productTable(): array
|
||||||
{
|
{
|
||||||
$product_items = collect($this->entity->line_items)->filter(function ($item) {
|
$product_items = collect($this->entity->line_items)->filter(function ($item) {
|
||||||
return $item->type_id == 1 || $item->type_id == 6 || $item->type_id == 5;
|
return $item->type_id == 1 || $item->type_id == 6 || $item->type_id == 5 || $item->type_id == 4;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (count($product_items) == 0) {
|
if (count($product_items) == 0) {
|
||||||
|
@ -277,7 +277,6 @@ class PaymentLinkService
|
|||||||
/**
|
/**
|
||||||
* @param Invoice $invoice
|
* @param Invoice $invoice
|
||||||
* @return true
|
* @return true
|
||||||
* @throws BindingResolutionException
|
|
||||||
*/
|
*/
|
||||||
public function planPaid(Invoice $invoice)
|
public function planPaid(Invoice $invoice)
|
||||||
{
|
{
|
||||||
|
@ -74,6 +74,12 @@ trait CleanLineItems
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(isset($item['notes']))
|
||||||
|
$item['notes'] = str_replace("</", "<-", $item['notes']);
|
||||||
|
|
||||||
|
if(isset($item['product_key']))
|
||||||
|
$item['product_key'] = str_replace("</", "<-", $item['product_key']);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('id', $item) || array_key_exists('_id', $item)) {
|
if (array_key_exists('id', $item) || array_key_exists('_id', $item)) {
|
||||||
|
@ -5120,7 +5120,7 @@ $lang = array(
|
|||||||
'set_private' => 'Set private',
|
'set_private' => 'Set private',
|
||||||
'individual' => 'Individual',
|
'individual' => 'Individual',
|
||||||
'business' => 'Business',
|
'business' => 'Business',
|
||||||
'partnership' => 'partnership',
|
'partnership' => 'Partnership',
|
||||||
'trust' => 'Trust',
|
'trust' => 'Trust',
|
||||||
'charity' => 'Charity',
|
'charity' => 'Charity',
|
||||||
'government' => 'Government',
|
'government' => 'Government',
|
||||||
|
14675
openapi/api-docs.yaml
14675
openapi/api-docs.yaml
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,6 @@ components:
|
|||||||
description: 'The total number of requests in a given time window.'
|
description: 'The total number of requests in a given time window.'
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
type: integer
|
||||||
components:
|
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
ApiKeyAuth:
|
ApiKeyAuth:
|
||||||
type: apiKey
|
type: apiKey
|
||||||
|
@ -1,72 +1,75 @@
|
|||||||
examples:
|
#examples:
|
||||||
Client:
|
# Client:
|
||||||
- id: Opnel5aKBz
|
# $ref: '#/components/schemas/Client'
|
||||||
user_id: Ua6Rw4pVbS
|
|
||||||
assigned_user_id: Ua6Rw4pVbS
|
# Client:
|
||||||
company_id: Co7Vn3yLmW
|
# id: Opnel5aKBz
|
||||||
name: "Jim's Housekeeping"
|
# user_id: Ua6Rw4pVbS
|
||||||
website: https://www.jims-housekeeping.com
|
# assigned_user_id: Ua6Rw4pVbS
|
||||||
private_notes: Client prefers email communication over phone calls
|
# company_id: Co7Vn3yLmW
|
||||||
client_hash: asdfkjhk342hjhbfdvmnfb1
|
# name: "Jim's Housekeeping"
|
||||||
industry_id: 5
|
# website: https://www.jims-housekeeping.com
|
||||||
size_id: 2
|
# private_notes: Client prefers email communication over phone calls
|
||||||
address1: 123 Main St
|
# client_hash: asdfkjhk342hjhbfdvmnfb1
|
||||||
address2: Apt 4B
|
# industry_id: 5
|
||||||
city: Beverly Hills
|
# size_id: 2
|
||||||
state: California
|
# address1: 123 Main St
|
||||||
postal_code: 90210
|
# address2: Apt 4B
|
||||||
phone: 555-3434-3434
|
# city: Beverly Hills
|
||||||
country_id: 1
|
# state: California
|
||||||
custom_value1: Email
|
# postal_code: 90210
|
||||||
custom_value2: John Doe
|
# phone: 555-3434-3434
|
||||||
custom_value3: Yes
|
# country_id: 1
|
||||||
custom_value4: $50,000
|
# custom_value1: Email
|
||||||
vat_number: VAT123456
|
# custom_value2: John Doe
|
||||||
id_number: ID123456
|
# custom_value3: Yes
|
||||||
number: CL-0001
|
# custom_value4: $50,000
|
||||||
shipping_address1: 5 Wallaby Way
|
# vat_number: VAT123456
|
||||||
shipping_address2: Suite 5
|
# id_number: ID123456
|
||||||
shipping_city: Perth
|
# number: CL-0001
|
||||||
shipping_state: Western Australia
|
# shipping_address1: 5 Wallaby Way
|
||||||
shipping_postal_code: 6110
|
# shipping_address2: Suite 5
|
||||||
shipping_country_id: 4
|
# shipping_city: Perth
|
||||||
is_deleted: false
|
# shipping_state: Western Australia
|
||||||
balance: 500.00
|
# shipping_postal_code: 6110
|
||||||
paid_to_date: 2000.00
|
# shipping_country_id: 4
|
||||||
credit_balance: 100.00
|
# is_deleted: false
|
||||||
last_login: 1628686031
|
# balance: 500.00
|
||||||
created_at: 1617629031
|
# paid_to_date: 2000.00
|
||||||
updated_at: 1628445631
|
# credit_balance: 100.00
|
||||||
group_settings_id: Opnel5aKBz
|
# last_login: 1628686031
|
||||||
routing_id: Opnel5aKBz3489-dfkiu-2239-sdsd
|
# created_at: 1617629031
|
||||||
is_tax_exempt: false
|
# updated_at: 1628445631
|
||||||
has_valid_vat_number: false
|
# group_settings_id: Opnel5aKBz
|
||||||
payment_balance: 100
|
# routing_id: Opnel5aKBz3489-dfkiu-2239-sdsd
|
||||||
contacts:
|
# is_tax_exempt: false
|
||||||
- id: Opnel5aKBz
|
# has_valid_vat_number: false
|
||||||
first_name: John
|
# payment_balance: 100
|
||||||
last_name: Doe
|
# contacts:
|
||||||
email: jim@gmail.com
|
# id: Opnel5aKBz
|
||||||
phone: 555-3434-3434
|
# first_name: John
|
||||||
send_invoice: true
|
# last_name: Doe
|
||||||
custom_value1: Email
|
# email: jim@gmail.com
|
||||||
custom_value2: John Doe
|
# phone: 555-3434-3434
|
||||||
custom_value3: Yes
|
# send_invoice: true
|
||||||
custom_value4: $50,000
|
# custom_value1: Email
|
||||||
is_primary: true
|
# custom_value2: John Doe
|
||||||
created_at: 1617629031
|
# custom_value3: Yes
|
||||||
updated_at: 1628445631
|
# custom_value4: $50,000
|
||||||
deleted_at: 1628445631
|
# is_primary: true
|
||||||
Meta:
|
# created_at: 1617629031
|
||||||
value:
|
# updated_at: 1628445631
|
||||||
pagination:
|
# deleted_at: 1628445631
|
||||||
total: 1
|
# Meta:
|
||||||
count: 1
|
# value:
|
||||||
per_page: 20
|
# pagination:
|
||||||
current_page: 1
|
# total: 1
|
||||||
total_pages: 1
|
# count: 1
|
||||||
links:
|
# per_page: 20
|
||||||
- first: https://invoicing.co/api/v1/invoices?page=1
|
# current_page: 1
|
||||||
- last: https://invoicing.co/api/v1/invoices?page=1
|
# total_pages: 1
|
||||||
- prev: null
|
# links:
|
||||||
- next: null
|
# - first: https://invoicing.co/api/v1/invoices?page=1
|
||||||
|
# - last: https://invoicing.co/api/v1/invoices?page=1
|
||||||
|
# - prev: null
|
||||||
|
# - next: null
|
@ -139,7 +139,7 @@
|
|||||||
description: The number of records to return for each request, default is 20
|
description: The number of records to return for each request, default is 20
|
||||||
required: false
|
required: false
|
||||||
schema:
|
schema:
|
||||||
type: int
|
type: integer
|
||||||
example: 20
|
example: 20
|
||||||
page_meta:
|
page_meta:
|
||||||
name: page
|
name: page
|
||||||
@ -147,7 +147,7 @@
|
|||||||
description: The page number to return for this request (when performing pagination), default is 1
|
description: The page number to return for this request (when performing pagination), default is 1
|
||||||
required: false
|
required: false
|
||||||
schema:
|
schema:
|
||||||
type: int
|
type: integer
|
||||||
example: 1
|
example: 1
|
||||||
include:
|
include:
|
||||||
name: include
|
name: include
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
Filters the entity list by entities that have been deleted.
|
Filters the entity list by entities that have been deleted.
|
||||||
required: false
|
required: false
|
||||||
schema:
|
schema:
|
||||||
type: booleans
|
type: boolean
|
||||||
example: ?is_deleted=true
|
example: ?is_deleted=true
|
||||||
vendor_id:
|
vendor_id:
|
||||||
name: vendor_id
|
name: vendor_id
|
||||||
|
@ -143,7 +143,7 @@
|
|||||||
type: boolean
|
type: boolean
|
||||||
example: true
|
example: true
|
||||||
default_auto_bill:
|
default_auto_bill:
|
||||||
type: enum
|
type: string
|
||||||
example: 'always'
|
example: 'always'
|
||||||
description: |
|
description: |
|
||||||
A flag determining whether to auto-bill clients by default
|
A flag determining whether to auto-bill clients by default
|
||||||
|
@ -7,11 +7,9 @@
|
|||||||
settings:
|
settings:
|
||||||
description: 'Settings that are used for the frontend applications to store user preferences / metadata'
|
description: 'Settings that are used for the frontend applications to store user preferences / metadata'
|
||||||
type: object
|
type: object
|
||||||
example: 'json object'
|
|
||||||
react_settings:
|
react_settings:
|
||||||
description: 'Dedicated settings object for the react web application'
|
description: 'Dedicated settings object for the react web application'
|
||||||
type: object'
|
type: object
|
||||||
example: 'json object'
|
|
||||||
is_owner:
|
is_owner:
|
||||||
description: 'Determines whether the user owns this company'
|
description: 'Determines whether the user owns this company'
|
||||||
type: boolean
|
type: boolean
|
||||||
|
@ -101,7 +101,7 @@
|
|||||||
type: number
|
type: number
|
||||||
format: float
|
format: float
|
||||||
example: '10.00'
|
example: '10.00'
|
||||||
readOnly:
|
readOnly: true
|
||||||
line_items:
|
line_items:
|
||||||
type: array
|
type: array
|
||||||
description: 'An array of objects which define the line items of the invoice'
|
description: 'An array of objects which define the line items of the invoice'
|
||||||
|
@ -31,6 +31,6 @@
|
|||||||
example: 1
|
example: 1
|
||||||
readOnly: true
|
readOnly: true
|
||||||
links:
|
links:
|
||||||
type: array
|
type: object
|
||||||
description: 'The pagination links'
|
description: 'The pagination links'
|
||||||
readOnly: true
|
readOnly: true
|
@ -10,99 +10,99 @@
|
|||||||
type: string
|
type: string
|
||||||
description: 'The hashed ID of the user assigned to this product.'
|
description: 'The hashed ID of the user assigned to this product.'
|
||||||
example: pR0j3
|
example: pR0j3
|
||||||
required: false
|
|
||||||
project_id:
|
project_id:
|
||||||
type: string
|
type: string
|
||||||
description: 'The hashed ID of the project that this product is associated with.'
|
description: 'The hashed ID of the project that this product is associated with.'
|
||||||
example: pR0j3
|
example: pR0j3
|
||||||
required: false
|
|
||||||
vendor_id:
|
vendor_id:
|
||||||
type: string
|
type: string
|
||||||
description: 'The hashed ID of the vendor that this product is associated with.'
|
description: 'The hashed ID of the vendor that this product is associated with.'
|
||||||
example: pR0j3
|
example: pR0j3
|
||||||
required: false
|
|
||||||
custom_value1:
|
custom_value1:
|
||||||
type: string
|
type: string
|
||||||
description: 'Custom value field 1.'
|
description: 'Custom value field 1.'
|
||||||
example: 'Custom value 1'
|
example: 'Custom value 1'
|
||||||
required: false
|
|
||||||
custom_value2:
|
custom_value2:
|
||||||
type: string
|
type: string
|
||||||
description: 'Custom value field 2.'
|
description: 'Custom value field 2.'
|
||||||
example: 'Custom value 2'
|
example: 'Custom value 2'
|
||||||
required: false
|
|
||||||
custom_value3:
|
custom_value3:
|
||||||
type: string
|
type: string
|
||||||
description: 'Custom value field 3.'
|
description: 'Custom value field 3.'
|
||||||
example: 'Custom value 3'
|
example: 'Custom value 3'
|
||||||
required: false
|
|
||||||
custom_value4:
|
custom_value4:
|
||||||
type: string
|
type: string
|
||||||
description: 'Custom value field 4.'
|
description: 'Custom value field 4.'
|
||||||
example: 'Custom value 4'
|
example: 'Custom value 4'
|
||||||
required: false
|
|
||||||
product_key:
|
product_key:
|
||||||
type: string
|
type: string
|
||||||
description: 'The product key.'
|
description: 'The product key.'
|
||||||
example: '1234'
|
example: '1234'
|
||||||
required: false
|
|
||||||
notes:
|
notes:
|
||||||
type: string
|
type: string
|
||||||
description: 'Notes about the product.'
|
description: 'Notes about the product.'
|
||||||
example: 'These are some notes about the product.'
|
example: 'These are some notes about the product.'
|
||||||
required: false
|
|
||||||
cost:
|
cost:
|
||||||
type: number
|
type: number
|
||||||
format: double
|
format: double
|
||||||
description: 'The cost of the product.'
|
description: 'The cost of the product.'
|
||||||
example: 10.0
|
example: 10.0
|
||||||
required: false
|
|
||||||
price:
|
price:
|
||||||
type: number
|
type: number
|
||||||
format: double
|
format: double
|
||||||
description: 'The price of the product.'
|
description: 'The price of the product.'
|
||||||
example: 20.0
|
example: 20.0
|
||||||
required: false
|
|
||||||
quantity:
|
quantity:
|
||||||
type: number
|
type: number
|
||||||
format: double
|
format: double
|
||||||
description: 'The quantity of the product.'
|
description: 'The quantity of the product.'
|
||||||
example: 5.0
|
example: 5.0
|
||||||
required: false
|
|
||||||
default: 1
|
default: 1
|
||||||
tax_name1:
|
tax_name1:
|
||||||
type: string
|
type: string
|
||||||
description: 'The name of tax 1.'
|
description: 'The name of tax 1.'
|
||||||
example: 'Tax 1'
|
example: 'Tax 1'
|
||||||
required: false
|
|
||||||
tax_rate1:
|
tax_rate1:
|
||||||
type: number
|
type: number
|
||||||
format: double
|
format: double
|
||||||
description: 'The rate of tax 1.'
|
description: 'The rate of tax 1.'
|
||||||
example: 10.0
|
example: 10.0
|
||||||
required: false
|
|
||||||
tax_name2:
|
tax_name2:
|
||||||
type: string
|
type: string
|
||||||
description: 'The name of tax 2.'
|
description: 'The name of tax 2.'
|
||||||
example: 'Tax 2'
|
example: 'Tax 2'
|
||||||
required: false
|
|
||||||
tax_rate2:
|
tax_rate2:
|
||||||
type: number
|
type: number
|
||||||
format: double
|
format: double
|
||||||
description: 'The rate of tax 2.'
|
description: 'The rate of tax 2.'
|
||||||
example: 5.0
|
example: 5.0
|
||||||
required: false
|
|
||||||
tax_name3:
|
tax_name3:
|
||||||
type: string
|
type: string
|
||||||
description: 'The name of tax 3.'
|
description: 'The name of tax 3.'
|
||||||
example: 'Tax 3'
|
example: 'Tax 3'
|
||||||
required: false
|
|
||||||
tax_rate3:
|
tax_rate3:
|
||||||
type: number
|
type: number
|
||||||
format: double
|
format: double
|
||||||
description: 'The rate of tax 3.'
|
description: 'The rate of tax 3.'
|
||||||
example: 0.0
|
example: 0.0
|
||||||
required: false
|
|
||||||
in_stock_quantity:
|
in_stock_quantity:
|
||||||
type: integer
|
type: integer
|
||||||
format: int32
|
format: int32
|
||||||
@ -114,32 +114,32 @@
|
|||||||
The query parameter ?update_in_stock_quantity=true **MUST** be passed if you wish to update this value manually.
|
The query parameter ?update_in_stock_quantity=true **MUST** be passed if you wish to update this value manually.
|
||||||
|
|
||||||
default: 0
|
default: 0
|
||||||
required: false
|
|
||||||
stock_notification:
|
stock_notification:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Indicates whether stock notifications are enabled for this product
|
description: Indicates whether stock notifications are enabled for this product
|
||||||
default: true
|
default: true
|
||||||
required: false
|
|
||||||
stock_notification_threshold:
|
stock_notification_threshold:
|
||||||
type: integer
|
type: integer
|
||||||
format: int32
|
format: int32
|
||||||
description: The minimum quantity threshold for which stock notifications will be triggered
|
description: The minimum quantity threshold for which stock notifications will be triggered
|
||||||
default: 0
|
default: 0
|
||||||
required: false
|
|
||||||
max_quantity:
|
max_quantity:
|
||||||
type: integer
|
type: integer
|
||||||
format: int32
|
format: int32
|
||||||
description: The maximum quantity that can be ordered for this product
|
description: The maximum quantity that can be ordered for this product
|
||||||
required: false
|
|
||||||
product_image:
|
product_image:
|
||||||
type: string
|
type: string
|
||||||
description: The URL of the product image
|
description: The URL of the product image
|
||||||
format: uri-reference
|
format: uri-reference
|
||||||
required: false
|
|
||||||
tax_id:
|
tax_id:
|
||||||
type: string
|
type: string
|
||||||
default: '1'
|
default: '1'
|
||||||
required: false
|
|
||||||
description: |
|
description: |
|
||||||
The tax category id for this product.'
|
The tax category id for this product.'
|
||||||
|
|
||||||
|
@ -36,7 +36,6 @@ tags:
|
|||||||
description: |
|
description: |
|
||||||
Endpoint definitions for interacting with vendors.
|
Endpoint definitions for interacting with vendors.
|
||||||
- name: Purchase Orders
|
- name: Purchase Orders
|
||||||
summary: Purchase Orders
|
|
||||||
description: |
|
description: |
|
||||||
Endpoint definitions for interacting with purchase orders.
|
Endpoint definitions for interacting with purchase orders.
|
||||||
- name: expenses
|
- name: expenses
|
||||||
|
@ -91,8 +91,6 @@ paths:
|
|||||||
summary: "Attempts authentication"
|
summary: "Attempts authentication"
|
||||||
description: "Returns a CompanyUser object on success"
|
description: "Returns a CompanyUser object on success"
|
||||||
operationId: postLogin
|
operationId: postLogin
|
||||||
security:
|
|
||||||
- []
|
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: "#/components/parameters/X-API-SECRET"
|
- $ref: "#/components/parameters/X-API-SECRET"
|
||||||
- $ref: "#/components/parameters/X-API-TOKEN"
|
- $ref: "#/components/parameters/X-API-TOKEN"
|
||||||
|
@ -125,12 +125,12 @@
|
|||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/Client'
|
$ref: '#/components/schemas/Client'
|
||||||
example:
|
example:
|
||||||
$ref: '#/components/examples/Client'
|
$ref: '#/components/schemas/Client'
|
||||||
meta:
|
meta:
|
||||||
type: object
|
type: object
|
||||||
$ref: '#/components/schemas/Meta'
|
$ref: '#/components/schemas/Meta'
|
||||||
example:
|
example:
|
||||||
$ref: '#/components/examples/Meta'
|
$ref: '#/components/schemas/Meta'
|
||||||
401:
|
401:
|
||||||
$ref: '#/components/responses/401'
|
$ref: '#/components/responses/401'
|
||||||
403:
|
403:
|
||||||
|
@ -437,7 +437,6 @@
|
|||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
action:
|
action:
|
||||||
required: true
|
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
The action to be performed, options include:
|
The action to be performed, options include:
|
||||||
@ -470,7 +469,6 @@
|
|||||||
- `send_email`
|
- `send_email`
|
||||||
Emails an array of invoices. Requires additional properties to be sent. `email_type`
|
Emails an array of invoices. Requires additional properties to be sent. `email_type`
|
||||||
ids:
|
ids:
|
||||||
required: true
|
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
description: "Array of hashed IDs to be bulk 'actioned - ['D2J234DFA','D2J234DFA','D2J234DFA']"
|
description: "Array of hashed IDs to be bulk 'actioned - ['D2J234DFA','D2J234DFA','D2J234DFA']"
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
@if($account && !$account->isPaid())
|
@if($account && !$account->isPaid())
|
||||||
<div>
|
<div>
|
||||||
<img src="{{ asset('images/invoiceninja-black-logo-2.png') }}"
|
<img src="{{ asset('images/invoiceninja-black-logo-2.png') }}"
|
||||||
class="border-b border-gray-100 h-18 pb-4" alt="Invoice Ninja logo">
|
class="border-b border-gray-100 h-18 pb-4" alt="Invoice Ninja logo" id="company_logo">
|
||||||
</div>
|
</div>
|
||||||
@elseif(isset($company) && !is_null($company))
|
@elseif(isset($company) && !is_null($company))
|
||||||
<div>
|
<div>
|
||||||
|
@ -77,7 +77,7 @@ span {
|
|||||||
<td>
|
<td>
|
||||||
<div class="product-information">
|
<div class="product-information">
|
||||||
<div class="item-details">
|
<div class="item-details">
|
||||||
|
|
||||||
<p class="overflow-ellipsis overflow-hidden px-1 mb-2">{!! $product['notes'] !!}</p>
|
<p class="overflow-ellipsis overflow-hidden px-1 mb-2">{!! $product['notes'] !!}</p>
|
||||||
<p class="mt-2">
|
<p class="mt-2">
|
||||||
@if($show_quantity)
|
@if($show_quantity)
|
||||||
@ -170,7 +170,7 @@ span {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div id="notes" class="py-10 border-b-2 border-fuschia-600" x-show="show_notes">
|
<div id="notes" class="py-10 border-b-2 border-fuschia-600" x-show="show_notes">
|
||||||
{!! html_entity_decode($entity->public_notes) !!}
|
{!! html_entity_decode(e($entity->public_notes)) !!}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -98,6 +98,11 @@
|
|||||||
|
|
||||||
@livewireStyles
|
@livewireStyles
|
||||||
|
|
||||||
|
@if((bool) \App\Utils\Ninja::isSelfHost() && isset($company))
|
||||||
|
<style>
|
||||||
|
{!! $company->settings->portal_custom_css !!}
|
||||||
|
</style>
|
||||||
|
@endif
|
||||||
<link rel="stylesheet" type="text/css" href="{{ asset('vendor/cookieconsent@3/cookieconsent.min.css') }}" defer>
|
<link rel="stylesheet" type="text/css" href="{{ asset('vendor/cookieconsent@3/cookieconsent.min.css') }}" defer>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user