diff --git a/VERSION.txt b/VERSION.txt index 71e9d46ab354..d042caea5ec9 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.8.15 \ No newline at end of file +5.8.16 \ No newline at end of file diff --git a/app/Console/Commands/CheckData.php b/app/Console/Commands/CheckData.php index 94a1c6e105ec..709ccecc0d59 100644 --- a/app/Console/Commands/CheckData.php +++ b/app/Console/Commands/CheckData.php @@ -918,7 +918,18 @@ class CheckData extends Command $p->saveQuietly(); - $this->logMessage("Fixing currency for # {$p->id}"); + $this->logMessage("Fixing currency for # {$p->id}"); + + }); + + Company::whereNull("subdomain") + ->cursor() + ->when(Ninja::isHosted()) + ->each(function ($c) { + $c->subdomain = MultiDB::randomSubdomainGenerator(); + $c->save(); + + $this->logMessage("Fixing subdomain for # {$c->id}"); }); diff --git a/app/Export/CSV/BaseExport.php b/app/Export/CSV/BaseExport.php index d40e4052f329..cc4fe21f5efd 100644 --- a/app/Export/CSV/BaseExport.php +++ b/app/Export/CSV/BaseExport.php @@ -832,6 +832,42 @@ class BaseExport return $query; } + protected function addClientFilter($query, $clients): Builder + { + $transformed_clients = $this->transformKeys(explode(',', $clients)); + + $query->whereIn('client_id', $transformed_clients); + + return $query; + } + + protected function addVendorFilter($query, $vendors): Builder + { + $transformed_vendors = $this->transformKeys(explode(',', $vendors)); + + $query->whereIn('vendor_id', $transformed_vendors); + + return $query; + } + + protected function addProjectFilter($query, $projects): Builder + { + $transformed_projects = $this->transformKeys(explode(',', $projects)); + + $query->whereIn('project_id', $transformed_projects); + + return $query; + } + + protected function addCategoryFilter($query, $expense_categories): Builder + { + $transformed_expense_categories = $this->transformKeys(explode(',', $expense_categories)); + + $query->whereIn('category_id', $transformed_expense_categories); + + return $query; + } + protected function addInvoiceStatusFilter($query, $status): Builder { diff --git a/app/Export/CSV/ExpenseExport.php b/app/Export/CSV/ExpenseExport.php index afa55a7a36ff..1ac3c96b777d 100644 --- a/app/Export/CSV/ExpenseExport.php +++ b/app/Export/CSV/ExpenseExport.php @@ -88,6 +88,22 @@ class ExpenseExport extends BaseExport $query = $this->addDateRange($query); + if(isset($this->input['clients'])) { + $query = $this->addClientFilter($query, $this->input['clients']); + } + + if(isset($this->input['vendors'])) { + $query = $this->addVendorFilter($query, $this->input['vendors']); + } + + if(isset($this->input['projects'])) { + $query = $this->addProjectFilter($query, $this->input['projects']); + } + + if(isset($this->input['categories'])) { + $query = $this->addCategoryFilter($query, $this->input['categories']); + } + return $query; } diff --git a/app/Http/Requests/Company/StoreCompanyRequest.php b/app/Http/Requests/Company/StoreCompanyRequest.php index ed29f3af0b0d..acab4c2a502e 100644 --- a/app/Http/Requests/Company/StoreCompanyRequest.php +++ b/app/Http/Requests/Company/StoreCompanyRequest.php @@ -11,13 +11,14 @@ namespace App\Http\Requests\Company; -use App\Http\Requests\Request; -use App\Http\ValidationRules\Company\ValidCompanyQuantity; -use App\Http\ValidationRules\Company\ValidSubdomain; -use App\Http\ValidationRules\ValidSettingsRule; -use App\Models\Company; use App\Utils\Ninja; +use App\Models\Company; +use App\Libraries\MultiDB; +use App\Http\Requests\Request; use App\Utils\Traits\MakesHash; +use App\Http\ValidationRules\ValidSettingsRule; +use App\Http\ValidationRules\Company\ValidSubdomain; +use App\Http\ValidationRules\Company\ValidCompanyQuantity; class StoreCompanyRequest extends Request { @@ -74,6 +75,10 @@ class StoreCompanyRequest extends Request $input['portal_domain'] = rtrim(strtolower($input['portal_domain']), "/"); } + if(Ninja::isHosted() && !isset($input['subdomain'])) { + $input['subdomain'] = MultiDB::randomSubdomainGenerator(); + } + $this->replace($input); } } diff --git a/app/Libraries/MultiDB.php b/app/Libraries/MultiDB.php index f03442c35402..fd028a03811c 100644 --- a/app/Libraries/MultiDB.php +++ b/app/Libraries/MultiDB.php @@ -557,9 +557,9 @@ class MultiDB $current_db = config('database.default'); do { - $length = 8; + $length = 10; $string = ''; - $vowels = ['a', 'e', 'i', 'o', 'u']; + $vowels = ['a', 'e', 'i', 'o', 'u', 'y']; $consonants = [ 'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z', @@ -568,7 +568,7 @@ class MultiDB $max = $length / 2; for ($i = 1; $i <= $max; $i++) { $string .= $consonants[rand(0, 19)]; - $string .= $vowels[rand(0, 4)]; + $string .= $vowels[rand(0, 5)]; } } while (! self::checkDomainAvailable($string)); diff --git a/app/Livewire/TasksTable.php b/app/Livewire/TasksTable.php index ea77f8588360..8e4c50ba984c 100644 --- a/app/Livewire/TasksTable.php +++ b/app/Livewire/TasksTable.php @@ -54,6 +54,7 @@ class TasksTable extends Component return render('components.livewire.tasks-table', [ 'tasks' => $query, + 'show_item_description' => auth()->guard('contact')->user()->company->invoice_task_item_description ?? false, ]); } } diff --git a/app/Mail/TemplateEmail.php b/app/Mail/TemplateEmail.php index f9f4b1562a21..7ccd29cac206 100644 --- a/app/Mail/TemplateEmail.php +++ b/app/Mail/TemplateEmail.php @@ -139,7 +139,7 @@ class TemplateEmail extends Mailable 'whitelabel' => $this->client->user->account->isPaid() ? true : false, 'logo' => $this->company->present()->logo($settings), 'links' => $this->build_email->getAttachmentLinks(), - 'email_preferences' => URL::signedRoute('client.email_preferences', ['entity' => $this->invitation->getEntityString(), 'invitation_key' => $this->invitation->key]), + 'email_preferences' => (Ninja::isHosted() && in_array($settings->email_sending_method, ['default', 'mailgun'])) ? URL::signedRoute('client.email_preferences', ['entity' => $this->invitation->getEntityString(), 'invitation_key' => $this->invitation->key]) : false, ]); foreach ($this->build_email->getAttachments() as $file) { diff --git a/app/Models/Task.php b/app/Models/Task.php index eaeaec0f3d18..16903f6fff62 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -316,8 +316,8 @@ class Task extends BaseModel $logged['end_date'] = ctrans('texts.running'); } - $logged['description'] = $log[2]; - $logged['billable'] = $log[3]; + $logged['description'] = $log[2] ?? ''; + $logged['billable'] = $log[3] ?? false; $logged['duration_raw'] = $duration; $logged['duration'] = gmdate("H:i:s", $duration); diff --git a/app/Repositories/ClientRepository.php b/app/Repositories/ClientRepository.php index d457eeefcd32..f7ec59ce7fba 100644 --- a/app/Repositories/ClientRepository.php +++ b/app/Repositories/ClientRepository.php @@ -70,7 +70,7 @@ class ClientRepository extends BaseRepository $client->settings = $client->saveSettings($data['settings'], $client); } - if (! $client->country_id) { + if (! $client->country_id || $client->country_id == 0) { /** @var \App\Models\Company $company **/ $company = Company::find($client->company_id); $client->country_id = $company->settings->country_id; diff --git a/app/Services/Email/EmailMailable.php b/app/Services/Email/EmailMailable.php index ada43bab730d..fd2831cb443b 100644 --- a/app/Services/Email/EmailMailable.php +++ b/app/Services/Email/EmailMailable.php @@ -11,13 +11,14 @@ namespace App\Services\Email; +use App\Utils\Ninja; use App\Models\Document; -use Illuminate\Mail\Attachment; use Illuminate\Mail\Mailable; -use Illuminate\Mail\Mailables\Content; -use Illuminate\Mail\Mailables\Envelope; -use Illuminate\Mail\Mailables\Headers; +use Illuminate\Mail\Attachment; use Illuminate\Support\Facades\URL; +use Illuminate\Mail\Mailables\Content; +use Illuminate\Mail\Mailables\Headers; +use Illuminate\Mail\Mailables\Envelope; class EmailMailable extends Mailable { @@ -77,7 +78,7 @@ class EmailMailable extends Mailable 'company' => $this->email_object->company, 'greeting' => '', 'links' => array_merge($this->email_object->links, $links->toArray()), - 'email_preferences' => $this->email_object->invitation + 'email_preferences' => (Ninja::isHosted() && in_array($this->email_object->settings->email_sending_method, ['default', 'mailgun']) && $this->email_object->invitation) ? URL::signedRoute('client.email_preferences', ['entity' => $this->email_object->invitation->getEntityString(), 'invitation_key' => $this->email_object->invitation->key]) : false, ] diff --git a/config/ninja.php b/config/ninja.php index dfbcd7b0b83c..f4ed7ccc5613 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -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.8.15'), - 'app_tag' => env('APP_TAG', '5.8.15'), + 'app_version' => env('APP_VERSION', '5.8.16'), + 'app_tag' => env('APP_TAG', '5.8.16'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false), diff --git a/openapi/api-docs.yaml b/openapi/api-docs.yaml index b4a2b5e46e27..3190be9b6fc4 100644 --- a/openapi/api-docs.yaml +++ b/openapi/api-docs.yaml @@ -1359,7 +1359,7 @@ paths: get: tags: - client_gateway_tokens - summary: "List of client tokens" + summary: "List of client payment tokens" description: "Lists client_gateway_tokens, search and filters allow fine grained lists to be generated.\n\n Query parameters can be added to performed more fine grained filtering of the client_gateway_tokens, these are handled by the ClientGatewayTokenFilters class which defines the methods available" operationId: getClientGatewayTokens parameters: @@ -1400,8 +1400,8 @@ paths: post: tags: - client_gateway_tokens - summary: "Adds a client" - description: "Adds an client to a company" + summary: "Adds a client payment token" + description: "Adds a client payment token to a company" operationId: storeClientGatewayToken parameters: - $ref: "#/components/parameters/X-API-TOKEN" @@ -1409,7 +1409,7 @@ paths: - $ref: "#/components/parameters/include" responses: 200: - description: "Returns the saved client object" + description: "Returns the saved client payment token object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -1433,8 +1433,8 @@ paths: get: tags: - client_gateway_tokens - summary: "Shows a client" - description: "Displays a client by id" + summary: "Shows a client payment token" + description: "Displays a client payment token by id" operationId: showClientGatewayToken parameters: - $ref: "#/components/parameters/X-API-TOKEN" @@ -1450,7 +1450,7 @@ paths: example: D2J234DFA responses: 200: - description: "Returns the cl.ient object" + description: "Returns the client payment token object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -1473,8 +1473,8 @@ paths: put: tags: - client_gateway_tokens - summary: "Updates a client" - description: "Handles the updating of a client by id" + summary: "Updates a client payment token" + description: "Handles the updating of a client payment token by id" operationId: updateClientGatewayToken parameters: - $ref: "#/components/parameters/X-API-TOKEN" @@ -1490,7 +1490,7 @@ paths: example: D2J234DFA responses: 200: - description: "Returns the client object" + description: "Returns the client payment token object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -1514,7 +1514,7 @@ paths: tags: - client_gateway_tokens summary: "Deletes a client" - description: "Handles the deletion of a client by id" + description: "Handles the deletion of a client payment token by id" operationId: deleteClientGatewayToken parameters: - $ref: "#/components/parameters/X-API-TOKEN" @@ -1550,7 +1550,7 @@ paths: get: tags: - client_gateway_tokens - summary: "Shows a client for editting" + summary: "Shows a client payment token for editting" description: "Displays a client by id" operationId: editClientGatewayToken parameters: @@ -1567,7 +1567,7 @@ paths: example: D2J234DFA responses: 200: - description: "Returns the client object" + description: "Returns the client payment token object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -1591,7 +1591,7 @@ paths: get: tags: - client_gateway_tokens - summary: "Gets a new blank client object" + summary: "Gets a new blank client payment token object" description: "Returns a blank object with default values" operationId: getClientGatewayTokensCreate parameters: @@ -1600,7 +1600,7 @@ paths: - $ref: "#/components/parameters/include" responses: 200: - description: "A blank client object" + description: "A blank client payment token object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" diff --git a/openapi/paths.yaml b/openapi/paths.yaml index 2ac54bf343c2..d52ad7af0002 100644 --- a/openapi/paths.yaml +++ b/openapi/paths.yaml @@ -1335,7 +1335,7 @@ paths: get: tags: - client_gateway_tokens - summary: "List of client tokens" + summary: "List of client payment tokens" description: "Lists client_gateway_tokens, search and filters allow fine grained lists to be generated.\n\n Query parameters can be added to performed more fine grained filtering of the client_gateway_tokens, these are handled by the ClientGatewayTokenFilters class which defines the methods available" operationId: getClientGatewayTokens parameters: @@ -1376,8 +1376,8 @@ paths: post: tags: - client_gateway_tokens - summary: "Adds a client" - description: "Adds an client to a company" + summary: "Adds a client payment token" + description: "Adds a client payment token to a company" operationId: storeClientGatewayToken parameters: - $ref: "#/components/parameters/X-API-TOKEN" @@ -1385,7 +1385,7 @@ paths: - $ref: "#/components/parameters/include" responses: 200: - description: "Returns the saved client object" + description: "Returns the saved client payment token object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -1409,8 +1409,8 @@ paths: get: tags: - client_gateway_tokens - summary: "Shows a client" - description: "Displays a client by id" + summary: "Shows a client payment token" + description: "Displays a client payment token by id" operationId: showClientGatewayToken parameters: - $ref: "#/components/parameters/X-API-TOKEN" @@ -1426,7 +1426,7 @@ paths: example: D2J234DFA responses: 200: - description: "Returns the cl.ient object" + description: "Returns the client payment token object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -1449,8 +1449,8 @@ paths: put: tags: - client_gateway_tokens - summary: "Updates a client" - description: "Handles the updating of a client by id" + summary: "Updates a client payment token" + description: "Handles the updating of a client payment token by id" operationId: updateClientGatewayToken parameters: - $ref: "#/components/parameters/X-API-TOKEN" @@ -1466,7 +1466,7 @@ paths: example: D2J234DFA responses: 200: - description: "Returns the client object" + description: "Returns the client payment token object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -1490,7 +1490,7 @@ paths: tags: - client_gateway_tokens summary: "Deletes a client" - description: "Handles the deletion of a client by id" + description: "Handles the deletion of a client payment token by id" operationId: deleteClientGatewayToken parameters: - $ref: "#/components/parameters/X-API-TOKEN" @@ -1526,7 +1526,7 @@ paths: get: tags: - client_gateway_tokens - summary: "Shows a client for editting" + summary: "Shows a client payment token for editting" description: "Displays a client by id" operationId: editClientGatewayToken parameters: @@ -1543,7 +1543,7 @@ paths: example: D2J234DFA responses: 200: - description: "Returns the client object" + description: "Returns the client payment token object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -1567,7 +1567,7 @@ paths: get: tags: - client_gateway_tokens - summary: "Gets a new blank client object" + summary: "Gets a new blank client payment token object" description: "Returns a blank object with default values" operationId: getClientGatewayTokensCreate parameters: @@ -1576,7 +1576,7 @@ paths: - $ref: "#/components/parameters/include" responses: 200: - description: "A blank client object" + description: "A blank client payment token object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" diff --git a/resources/views/portal/ninja2020/components/livewire/tasks-table.blade.php b/resources/views/portal/ninja2020/components/livewire/tasks-table.blade.php index eeae072b6c18..ab2acb24397b 100644 --- a/resources/views/portal/ninja2020/components/livewire/tasks-table.blade.php +++ b/resources/views/portal/ninja2020/components/livewire/tasks-table.blade.php @@ -12,7 +12,7 @@
- +
- @forelse($tasks as $task) + @foreach($tasks as $task) - @empty + @if($show_item_description) + + @endif + @endforeach + @if($tasks->count() == 0) - @endforelse + @endif
@@ -38,7 +38,7 @@
{{ \Illuminate\Support\Str::limit($task->description, 80) }} @@ -63,13 +63,56 @@ {{ \Carbon\CarbonInterval::seconds($task->calcDuration())->cascade()->forHumans() }}
+ + + + + + + + + + @foreach($task->processLogsExpandedNotation() as $log) + @if(strlen($log['description']) > 1) + + + + + + @endif + @endforeach + +
+ + {{ ctrans('texts.date') }} + + + + {{ ctrans('texts.duration') }} + + + + {{ ctrans('texts.description') }} + +
+ {{ $log['start_date']}} + + {{ $log['duration']}} + + {!! nl2br(e($log['description'])) !!} +
+
{{ ctrans('texts.no_results') }}