1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-11 05:32:39 +01:00

Merge pull request #9193 from turbo124/v5-develop

v5.8.16
This commit is contained in:
David Bomba 2024-01-25 15:12:46 +11:00 committed by GitHub
commit 18f1e3c1b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 168 additions and 55 deletions

View File

@ -1 +1 @@
5.8.15
5.8.16

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,
]

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@
</div>
<div class="-my-2 py-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8">
<div class="align-middle inline-block min-w-full overflow-hidden rounded">
<table class="min-w-full shadow rounded border border-gray-200 mt-4 credits-table">
<table class="min-w-full shadow rounded border border-gray-200 mt-4 credits-table bg-white">
<thead>
<tr>
<th class="px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-white uppercase border-b border-gray-200 bg-primary">
@ -38,7 +38,7 @@
</tr>
</thead>
<tbody>
@forelse($tasks as $task)
@foreach($tasks as $task)
<tr class="bg-white group hover:bg-gray-100">
<td class="px-6 py-4 whitespace-nowrap text-sm leading-5 text-gray-500">
{{ \Illuminate\Support\Str::limit($task->description, 80) }}
@ -63,13 +63,56 @@
{{ \Carbon\CarbonInterval::seconds($task->calcDuration())->cascade()->forHumans() }}
</td>
</tr>
@empty
@if($show_item_description)
<tr><td width="100%" colspan="4">
<table class="min-w-full ml-5">
<thead>
<tr>
<th class="px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-white uppercase border-b border-gray-200 bg-gray-500">
<span>
{{ ctrans('texts.date') }}
</span>
</th>
<th class="px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-white uppercase border-b border-gray-200 bg-gray-500">
<span>
{{ ctrans('texts.duration') }}
</span>
</th>
<th colspan="4" class="px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-white uppercase border-b border-gray-200 bg-gray-500">
<span>
{{ ctrans('texts.description') }}
</span>
</th>
</tr>
</thead>
<tbody>
@foreach($task->processLogsExpandedNotation() as $log)
@if(strlen($log['description']) > 1)
<tr class="bg-white group border-b border-gray-100">
<td class="px-6 py-4 text-sm leading-5 text-gray-500 w-1/6">
{{ $log['start_date']}}
</td>
<td class="px-6 py-4 text-sm leading-5 text-gray-500 w-1/6">
{{ $log['duration']}}
</td>
<td colspan="4" class="px-6 py-4 text-sm leading-5 text-gray-500 w-4/6">
{!! nl2br(e($log['description'])) !!}
</td>
</tr>
@endif
@endforeach
</tbody>
</table>
</td></tr>
@endif
@endforeach
@if($tasks->count() == 0)
<tr class="bg-white group hover:bg-gray-100">
<td class="px-6 py-4 whitespace-nowrap text-sm leading-5 text-gray-500" colspan="100%">
{{ ctrans('texts.no_results') }}
</td>
</tr>
@endforelse
@endif
</tbody>
</table>
</div>