mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-10-07 21:33:18 +01:00
Updated Laravel to v13
This commit is contained in:
committed by
Constantin Graf
parent
12cc3f27f8
commit
2dadf1ef02
@@ -23,7 +23,7 @@ use App\Service\Import\Importers\ImportException;
|
|||||||
use App\Service\Import\Importers\ReportDto;
|
use App\Service\Import\Importers\ReportDto;
|
||||||
use App\Service\Import\ImportService;
|
use App\Service\Import\ImportService;
|
||||||
use App\Service\TimezoneService;
|
use App\Service\TimezoneService;
|
||||||
use Brick\Money\ISOCurrencyProvider;
|
use Brick\Money\IsoCurrencyProvider;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
use Filament\Actions\DeleteAction;
|
use Filament\Actions\DeleteAction;
|
||||||
@@ -89,7 +89,7 @@ class OrganizationResource extends Resource
|
|||||||
Select::make('currency')
|
Select::make('currency')
|
||||||
->label('Currency')
|
->label('Currency')
|
||||||
->options(function (): array {
|
->options(function (): array {
|
||||||
$currencies = ISOCurrencyProvider::getInstance()->getAvailableCurrencies();
|
$currencies = IsoCurrencyProvider::getInstance()->getAvailableCurrencies();
|
||||||
$select = [];
|
$select = [];
|
||||||
foreach ($currencies as $currency) {
|
foreach ($currencies as $currency) {
|
||||||
$select[$currency->getCurrencyCode()] = $currency->getName().' ('.$currency->getCurrencyCode().')';
|
$select[$currency->getCurrencyCode()] = $currency->getName().' ('.$currency->getCurrencyCode().')';
|
||||||
|
|||||||
@@ -98,9 +98,10 @@ class UsersRelationManager extends RelationManager
|
|||||||
app(BillableRateService::class)->updateTimeEntriesBillableRateForMember($member);
|
app(BillableRateService::class)->updateTimeEntriesBillableRateForMember($member);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($data['role'] !== $member->role) {
|
$newRole = $data['role'] instanceof Role ? $data['role'] : Role::from($data['role']);
|
||||||
|
if ($newRole->value !== $member->role) {
|
||||||
try {
|
try {
|
||||||
app(MemberService::class)->changeRole($member, $organization, ($data['role'] instanceof Role ? $data['role'] : Role::from($data['role'])), true);
|
app(MemberService::class)->changeRole($member, $organization, $newRole, true);
|
||||||
} catch (ApiException $exception) {
|
} catch (ApiException $exception) {
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->danger()
|
->danger()
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use App\Models\User;
|
|||||||
use App\Service\DeletionService;
|
use App\Service\DeletionService;
|
||||||
use App\Service\TimezoneService;
|
use App\Service\TimezoneService;
|
||||||
use App\Service\UserService;
|
use App\Service\UserService;
|
||||||
use Brick\Money\ISOCurrencyProvider;
|
use Brick\Money\IsoCurrencyProvider;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Filament\Actions\BulkAction;
|
use Filament\Actions\BulkAction;
|
||||||
use Filament\Actions\DeleteAction;
|
use Filament\Actions\DeleteAction;
|
||||||
@@ -115,7 +115,7 @@ class UserResource extends Resource
|
|||||||
Select::make('currency')
|
Select::make('currency')
|
||||||
->label('Currency (Personal Organization)')
|
->label('Currency (Personal Organization)')
|
||||||
->options(function (): array {
|
->options(function (): array {
|
||||||
$currencies = ISOCurrencyProvider::getInstance()->getAvailableCurrencies();
|
$currencies = IsoCurrencyProvider::getInstance()->getAvailableCurrencies();
|
||||||
$select = [];
|
$select = [];
|
||||||
foreach ($currencies as $currency) {
|
foreach ($currencies as $currency) {
|
||||||
$select[$currency->getCurrencyCode()] = $currency->getName().' ('.$currency->getCurrencyCode().')';
|
$select[$currency->getCurrencyCode()] = $currency->getName().' ('.$currency->getCurrencyCode().')';
|
||||||
|
|||||||
@@ -59,9 +59,10 @@ class OrganizationsRelationManager extends RelationManager
|
|||||||
/** @var Member $member */
|
/** @var Member $member */
|
||||||
$member = $record->getRelation('membership');
|
$member = $record->getRelation('membership');
|
||||||
|
|
||||||
if ($data['role'] !== $member->role) {
|
$newRole = $data['role'] instanceof Role ? $data['role'] : Role::from($data['role']);
|
||||||
|
if ($newRole->value !== $member->role) {
|
||||||
try {
|
try {
|
||||||
app(MemberService::class)->changeRole($member, $record, ($data['role'] instanceof Role ? $data['role'] : Role::from($data['role'])), true);
|
app(MemberService::class)->changeRole($member, $record, $newRole, true);
|
||||||
} catch (ApiException $exception) {
|
} catch (ApiException $exception) {
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->danger()
|
->danger()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace App\Http\Controllers\Api\V1;
|
|||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Service\CurrencyService;
|
use App\Service\CurrencyService;
|
||||||
use Brick\Money\Currency;
|
use Brick\Money\Currency;
|
||||||
use Brick\Money\ISOCurrencyProvider;
|
use Brick\Money\IsoCurrencyProvider;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
||||||
class CurrencyController extends Controller
|
class CurrencyController extends Controller
|
||||||
@@ -29,7 +29,7 @@ class CurrencyController extends Controller
|
|||||||
'name' => $currency->getName(),
|
'name' => $currency->getName(),
|
||||||
'symbol' => $currencyService->getCurrencySymbol($currency->getCurrencyCode()),
|
'symbol' => $currencyService->getCurrencySymbol($currency->getCurrencyCode()),
|
||||||
],
|
],
|
||||||
ISOCurrencyProvider::getInstance()->getAvailableCurrencies()
|
IsoCurrencyProvider::getInstance()->getAvailableCurrencies()
|
||||||
));
|
));
|
||||||
|
|
||||||
return response()->json($currencies);
|
return response()->json($currencies);
|
||||||
|
|||||||
@@ -191,8 +191,9 @@ class TimeEntryController extends Controller
|
|||||||
$roundingMinutes = $canAccessPremiumFeatures ? $request->getRoundingMinutes() : null;
|
$roundingMinutes = $canAccessPremiumFeatures ? $request->getRoundingMinutes() : null;
|
||||||
if ($roundingType !== null && $roundingMinutes !== null) {
|
if ($roundingType !== null && $roundingMinutes !== null) {
|
||||||
$select = array_diff($select, ['start', 'end']);
|
$select = array_diff($select, ['start', 'end']);
|
||||||
$select[] = DB::raw(app(TimeEntryService::class)->getStartSelectRawForRounding($roundingType, $roundingMinutes).' as start');
|
// These SQL fragments are generated from a rounding enum and validated minute count.
|
||||||
$select[] = DB::raw(app(TimeEntryService::class)->getEndSelectRawForRounding($roundingType, $roundingMinutes).' as end');
|
$select[] = DB::raw(app(TimeEntryService::class)->getStartSelectRawForRounding($roundingType, $roundingMinutes).' as start'); // @phpstan-ignore argument.type
|
||||||
|
$select[] = DB::raw(app(TimeEntryService::class)->getEndSelectRawForRounding($roundingType, $roundingMinutes).' as end'); // @phpstan-ignore argument.type
|
||||||
}
|
}
|
||||||
$timeEntriesQuery = TimeEntry::query()
|
$timeEntriesQuery = TimeEntry::query()
|
||||||
->whereBelongsTo($organization, 'organization')
|
->whereBelongsTo($organization, 'organization')
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace App\Http\Controllers\Web;
|
|||||||
|
|
||||||
use App\Models\Organization;
|
use App\Models\Organization;
|
||||||
use Brick\Money\Currency;
|
use Brick\Money\Currency;
|
||||||
use Brick\Money\ISOCurrencyProvider;
|
use Brick\Money\IsoCurrencyProvider;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@@ -53,7 +53,7 @@ class OrganizationController extends Controller
|
|||||||
],
|
],
|
||||||
'currencies' => array_map(function (Currency $currency): string {
|
'currencies' => array_map(function (Currency $currency): string {
|
||||||
return $currency->getName();
|
return $currency->getName();
|
||||||
}, ISOCurrencyProvider::getInstance()->getAvailableCurrencies()),
|
}, IsoCurrencyProvider::getInstance()->getAvailableCurrencies()),
|
||||||
'permissions' => [
|
'permissions' => [
|
||||||
'canDeleteTeam' => $this->hasPermission($organization, 'organizations:delete'),
|
'canDeleteTeam' => $this->hasPermission($organization, 'organizations:delete'),
|
||||||
'canUpdateTeam' => $this->hasPermission($organization, 'organizations:update'),
|
'canUpdateTeam' => $this->hasPermission($organization, 'organizations:update'),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use App\Http\Middleware\EnsureEmailIsVerified;
|
|||||||
use App\Http\Middleware\ForceHttps;
|
use App\Http\Middleware\ForceHttps;
|
||||||
use App\Http\Middleware\ForceJsonResponse;
|
use App\Http\Middleware\ForceJsonResponse;
|
||||||
use App\Http\Middleware\HandleInertiaRequests;
|
use App\Http\Middleware\HandleInertiaRequests;
|
||||||
|
use App\Http\Middleware\PreventRequestForgery;
|
||||||
use App\Http\Middleware\PreventRequestsDuringMaintenance;
|
use App\Http\Middleware\PreventRequestsDuringMaintenance;
|
||||||
use App\Http\Middleware\RedirectIfAuthenticated;
|
use App\Http\Middleware\RedirectIfAuthenticated;
|
||||||
use App\Http\Middleware\ShareInertiaData;
|
use App\Http\Middleware\ShareInertiaData;
|
||||||
@@ -18,7 +19,6 @@ use App\Http\Middleware\TrimStrings;
|
|||||||
use App\Http\Middleware\TrustHosts;
|
use App\Http\Middleware\TrustHosts;
|
||||||
use App\Http\Middleware\TrustProxies;
|
use App\Http\Middleware\TrustProxies;
|
||||||
use App\Http\Middleware\ValidateSignature;
|
use App\Http\Middleware\ValidateSignature;
|
||||||
use App\Http\Middleware\VerifyCsrfToken;
|
|
||||||
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
|
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
|
||||||
use Illuminate\Auth\Middleware\Authorize;
|
use Illuminate\Auth\Middleware\Authorize;
|
||||||
use Illuminate\Auth\Middleware\RequirePassword;
|
use Illuminate\Auth\Middleware\RequirePassword;
|
||||||
@@ -68,7 +68,7 @@ class Kernel extends HttpKernel
|
|||||||
AddQueuedCookiesToResponse::class,
|
AddQueuedCookiesToResponse::class,
|
||||||
StartSession::class,
|
StartSession::class,
|
||||||
ShareErrorsFromSession::class,
|
ShareErrorsFromSession::class,
|
||||||
VerifyCsrfToken::class,
|
PreventRequestForgery::class,
|
||||||
SubstituteBindings::class,
|
SubstituteBindings::class,
|
||||||
HandleInertiaRequests::class,
|
HandleInertiaRequests::class,
|
||||||
ShareInertiaData::class,
|
ShareInertiaData::class,
|
||||||
|
|||||||
22
app/Http/Middleware/AuthenticateFilamentPanel.php
Normal file
22
app/Http/Middleware/AuthenticateFilamentPanel.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Filament\Http\Middleware\Authenticate as Middleware;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class AuthenticateFilamentPanel extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The admin panel has no login page of its own (it shares the app's
|
||||||
|
* session-based login), so Filament::getLoginUrl() always returns null.
|
||||||
|
* Fall back to the app's login route so guests are redirected instead
|
||||||
|
* of receiving an empty 401 response.
|
||||||
|
*/
|
||||||
|
protected function redirectTo($request): ?string
|
||||||
|
{
|
||||||
|
return parent::redirectTo($request) ?? ($request->expectsJson() ? null : route('login'));
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/Http/Middleware/PreventRequestForgery.php
Normal file
19
app/Http/Middleware/PreventRequestForgery.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery as Middleware;
|
||||||
|
|
||||||
|
class PreventRequestForgery extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The URIs that should be excluded.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
|
||||||
|
|
||||||
class VerifyCsrfToken extends Middleware
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The URIs that should be excluded from CSRF verification.
|
|
||||||
*
|
|
||||||
* @var array<int, string>
|
|
||||||
*/
|
|
||||||
protected $except = [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,6 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use OwenIt\Auditing\Contracts\Auditable as AuditableContract;
|
use OwenIt\Auditing\Contracts\Auditable as AuditableContract;
|
||||||
@@ -100,7 +99,7 @@ class Organization extends Model implements AuditableContract
|
|||||||
/**
|
/**
|
||||||
* Get all the users that belong to the team.
|
* Get all the users that belong to the team.
|
||||||
*
|
*
|
||||||
* @return BelongsToMany<User, $this, Pivot, 'membership'>
|
* @return BelongsToMany<User, $this, Member, 'membership'>
|
||||||
*/
|
*/
|
||||||
public function users(): BelongsToMany
|
public function users(): BelongsToMany
|
||||||
{
|
{
|
||||||
@@ -133,7 +132,7 @@ class Organization extends Model implements AuditableContract
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return BelongsToMany<User, $this, Pivot, 'membership'>
|
* @return BelongsToMany<User, $this, Member, 'membership'>
|
||||||
*/
|
*/
|
||||||
public function realUsers(): BelongsToMany
|
public function realUsers(): BelongsToMany
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
@@ -175,7 +174,7 @@ class User extends Authenticatable implements AuditableContract, FilamentUser, M
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return BelongsToMany<Organization, $this, Pivot, 'membership'>
|
* @return BelongsToMany<Organization, $this, Member, 'membership'>
|
||||||
*/
|
*/
|
||||||
public function organizations(): BelongsToMany
|
public function organizations(): BelongsToMany
|
||||||
{
|
{
|
||||||
@@ -190,7 +189,7 @@ class User extends Authenticatable implements AuditableContract, FilamentUser, M
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return BelongsToMany<Organization, $this, Pivot, 'membership'>
|
* @return BelongsToMany<Organization, $this, Member, 'membership'>
|
||||||
*/
|
*/
|
||||||
public function ownedOrganizations(): BelongsToMany
|
public function ownedOrganizations(): BelongsToMany
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use App\Filament\Widgets\ServerOverview;
|
|||||||
use App\Filament\Widgets\TimeEntriesCreated;
|
use App\Filament\Widgets\TimeEntriesCreated;
|
||||||
use App\Filament\Widgets\TimeEntriesImported;
|
use App\Filament\Widgets\TimeEntriesImported;
|
||||||
use App\Filament\Widgets\UserRegistrations;
|
use App\Filament\Widgets\UserRegistrations;
|
||||||
use Filament\Http\Middleware\Authenticate;
|
use App\Http\Middleware\AuthenticateFilamentPanel;
|
||||||
use Filament\Http\Middleware\DisableBladeIconComponents;
|
use Filament\Http\Middleware\DisableBladeIconComponents;
|
||||||
use Filament\Http\Middleware\DispatchServingFilamentEvent;
|
use Filament\Http\Middleware\DispatchServingFilamentEvent;
|
||||||
use Filament\Navigation\NavigationGroup;
|
use Filament\Navigation\NavigationGroup;
|
||||||
@@ -19,7 +19,7 @@ use Filament\PanelProvider;
|
|||||||
use Filament\Support\Colors\Color;
|
use Filament\Support\Colors\Color;
|
||||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||||
use Illuminate\Cookie\Middleware\EncryptCookies;
|
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
|
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
|
||||||
use Illuminate\Routing\Middleware\SubstituteBindings;
|
use Illuminate\Routing\Middleware\SubstituteBindings;
|
||||||
use Illuminate\Session\Middleware\AuthenticateSession;
|
use Illuminate\Session\Middleware\AuthenticateSession;
|
||||||
use Illuminate\Session\Middleware\StartSession;
|
use Illuminate\Session\Middleware\StartSession;
|
||||||
@@ -71,6 +71,9 @@ class AdminPanelProvider extends PanelProvider
|
|||||||
NavigationGroup::make()
|
NavigationGroup::make()
|
||||||
->label('Auth')
|
->label('Auth')
|
||||||
->collapsed(),
|
->collapsed(),
|
||||||
|
NavigationGroup::make()
|
||||||
|
->label('Self Hosting')
|
||||||
|
->collapsed(),
|
||||||
])
|
])
|
||||||
->middleware([
|
->middleware([
|
||||||
EncryptCookies::class,
|
EncryptCookies::class,
|
||||||
@@ -78,13 +81,13 @@ class AdminPanelProvider extends PanelProvider
|
|||||||
StartSession::class,
|
StartSession::class,
|
||||||
AuthenticateSession::class,
|
AuthenticateSession::class,
|
||||||
ShareErrorsFromSession::class,
|
ShareErrorsFromSession::class,
|
||||||
VerifyCsrfToken::class,
|
PreventRequestForgery::class,
|
||||||
SubstituteBindings::class,
|
SubstituteBindings::class,
|
||||||
DisableBladeIconComponents::class,
|
DisableBladeIconComponents::class,
|
||||||
DispatchServingFilamentEvent::class,
|
DispatchServingFilamentEvent::class,
|
||||||
])
|
])
|
||||||
->authMiddleware([
|
->authMiddleware([
|
||||||
Authenticate::class,
|
AuthenticateFilamentPanel::class,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$modules = Module::allEnabled();
|
$modules = Module::allEnabled();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Rules;
|
namespace App\Rules;
|
||||||
|
|
||||||
use Brick\Money\ISOCurrencyProvider;
|
use Brick\Money\IsoCurrencyProvider;
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Contracts\Validation\ValidationRule;
|
use Illuminate\Contracts\Validation\ValidationRule;
|
||||||
use Illuminate\Translation\PotentiallyTranslatedString;
|
use Illuminate\Translation\PotentiallyTranslatedString;
|
||||||
@@ -24,7 +24,7 @@ class CurrencyRule implements ValidationRule
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$currencies = ISOCurrencyProvider::getInstance()->getAvailableCurrencies();
|
$currencies = IsoCurrencyProvider::getInstance()->getAvailableCurrencies();
|
||||||
if (array_key_exists($value, $currencies)) {
|
if (array_key_exists($value, $currencies)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Service;
|
namespace App\Service;
|
||||||
|
|
||||||
use Brick\Money\ISOCurrencyProvider;
|
use Brick\Money\IsoCurrencyProvider;
|
||||||
use Brick\Money\Money;
|
use Brick\Money\Money;
|
||||||
|
|
||||||
class CurrencyService
|
class CurrencyService
|
||||||
@@ -378,7 +378,7 @@ class CurrencyService
|
|||||||
|
|
||||||
public function getRandomCurrencyCode(): string
|
public function getRandomCurrencyCode(): string
|
||||||
{
|
{
|
||||||
$currencies = ISOCurrencyProvider::getInstance()->getAvailableCurrencies();
|
$currencies = IsoCurrencyProvider::getInstance()->getAvailableCurrencies();
|
||||||
$currencyCodes = array_keys($currencies);
|
$currencyCodes = array_keys($currencies);
|
||||||
|
|
||||||
return $currencyCodes[array_rand($currencyCodes)];
|
return $currencyCodes[array_rand($currencyCodes)];
|
||||||
|
|||||||
@@ -152,12 +152,13 @@ class DashboardService
|
|||||||
|
|
||||||
$possibleDays = $this->lastDays($days, $timezone);
|
$possibleDays = $this->lastDays($days, $timezone);
|
||||||
|
|
||||||
|
// The SQL fragment contains only a timezone offset calculated as an integer.
|
||||||
$query = TimeEntry::query()
|
$query = TimeEntry::query()
|
||||||
->select(DB::raw('DATE('.$dateWithTimeZone.') as date, round(sum(extract(epoch from (coalesce("end", now()) - start)))) as aggregate'))
|
->select(DB::raw('DATE('.$dateWithTimeZone.') as date, round(sum(extract(epoch from (coalesce("end", now()) - start)))) as aggregate')) // @phpstan-ignore argument.type
|
||||||
->where('user_id', '=', $user->getKey())
|
->where('user_id', '=', $user->getKey())
|
||||||
->where('organization_id', '=', $organization->getKey())
|
->where('organization_id', '=', $organization->getKey())
|
||||||
->workTime()
|
->workTime()
|
||||||
->groupBy(DB::raw('DATE('.$dateWithTimeZone.')'))
|
->groupBy(DB::raw('DATE('.$dateWithTimeZone.')')) // @phpstan-ignore argument.type
|
||||||
->orderBy('date');
|
->orderBy('date');
|
||||||
|
|
||||||
$query = $this->constrainDateByPossibleDates($query, $possibleDays, $timezone);
|
$query = $this->constrainDateByPossibleDates($query, $possibleDays, $timezone);
|
||||||
@@ -194,12 +195,13 @@ class DashboardService
|
|||||||
}
|
}
|
||||||
$possibleDays = $this->daysOfThisWeek($timezone, $user->week_start);
|
$possibleDays = $this->daysOfThisWeek($timezone, $user->week_start);
|
||||||
|
|
||||||
|
// The SQL fragment contains only a timezone offset calculated as an integer.
|
||||||
$query = TimeEntry::query()
|
$query = TimeEntry::query()
|
||||||
->select(DB::raw('DATE('.$dateWithTimeZone.') as date, round(sum(extract(epoch from (coalesce("end", now()) - start)))) as aggregate'))
|
->select(DB::raw('DATE('.$dateWithTimeZone.') as date, round(sum(extract(epoch from (coalesce("end", now()) - start)))) as aggregate')) // @phpstan-ignore argument.type
|
||||||
->where('user_id', '=', $user->getKey())
|
->where('user_id', '=', $user->getKey())
|
||||||
->where('organization_id', '=', $organization->getKey())
|
->where('organization_id', '=', $organization->getKey())
|
||||||
->workTime()
|
->workTime()
|
||||||
->groupBy(DB::raw('DATE('.$dateWithTimeZone.')'))
|
->groupBy(DB::raw('DATE('.$dateWithTimeZone.')')) // @phpstan-ignore argument.type
|
||||||
->orderBy('date');
|
->orderBy('date');
|
||||||
|
|
||||||
$query = $this->constrainDateByPossibleDates($query, $possibleDays, $timezone);
|
$query = $this->constrainDateByPossibleDates($query, $possibleDays, $timezone);
|
||||||
|
|||||||
@@ -75,8 +75,9 @@ class TimeEntryAggregationService
|
|||||||
$startRawSelect = app(TimeEntryService::class)->getStartSelectRawForRounding($roundingType, $roundingMinutes);
|
$startRawSelect = app(TimeEntryService::class)->getStartSelectRawForRounding($roundingType, $roundingMinutes);
|
||||||
$endRawSelect = app(TimeEntryService::class)->getEndSelectRawForRounding($roundingType, $roundingMinutes);
|
$endRawSelect = app(TimeEntryService::class)->getEndSelectRawForRounding($roundingType, $roundingMinutes);
|
||||||
|
|
||||||
|
// SQL fragments come from enum cases, integer offsets, and formatted dates.
|
||||||
$timeEntriesQuery->selectRaw(
|
$timeEntriesQuery->selectRaw(
|
||||||
($group1Select !== null ? $group1Select.' as group_1,' : '').
|
($group1Select !== null ? $group1Select.' as group_1,' : ''). // @phpstan-ignore argument.type
|
||||||
($group2Select !== null ? $group2Select.' as group_2,' : '').
|
($group2Select !== null ? $group2Select.' as group_2,' : '').
|
||||||
' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')))) as aggregate,'.
|
' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')))) as aggregate,'.
|
||||||
' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')) * (coalesce(billable_rate, 0)::float/60/60))) as cost'
|
' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')) * (coalesce(billable_rate, 0)::float/60/60))) as cost'
|
||||||
@@ -105,7 +106,7 @@ class TimeEntryAggregationService
|
|||||||
$baseTotalsPerGroup1Query = $baseTotalsQuery->clone();
|
$baseTotalsPerGroup1Query = $baseTotalsQuery->clone();
|
||||||
$baseTotalsPerGroup1 = $baseTotalsPerGroup1Query
|
$baseTotalsPerGroup1 = $baseTotalsPerGroup1Query
|
||||||
->selectRaw(
|
->selectRaw(
|
||||||
$group1Select.' as group_1,'.
|
$group1Select.' as group_1,'. // @phpstan-ignore argument.type
|
||||||
' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')))) as aggregate,'.
|
' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')))) as aggregate,'.
|
||||||
' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')) * (coalesce(billable_rate, 0)::float/60/60))) as cost'
|
' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')) * (coalesce(billable_rate, 0)::float/60/60))) as cost'
|
||||||
)
|
)
|
||||||
@@ -170,7 +171,7 @@ class TimeEntryAggregationService
|
|||||||
// Reset selects and ordering on the cloned base query
|
// Reset selects and ordering on the cloned base query
|
||||||
$baseTotals = $baseTotalsQuery
|
$baseTotals = $baseTotalsQuery
|
||||||
->selectRaw(
|
->selectRaw(
|
||||||
' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')))) as aggregate,'.
|
' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')))) as aggregate,'. // @phpstan-ignore argument.type
|
||||||
' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')) * (coalesce(billable_rate, 0)::float/60/60))) as cost'
|
' round(sum(extract(epoch from ('.$endRawSelect.' - '.$startRawSelect.')) * (coalesce(billable_rate, 0)::float/60/60))) as cost'
|
||||||
)
|
)
|
||||||
->first();
|
->first();
|
||||||
|
|||||||
@@ -7,22 +7,22 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "8.3.*",
|
"php": "8.3.*",
|
||||||
"ext-zip": "*",
|
"ext-zip": "*",
|
||||||
"brick/money": "^0.11.2",
|
"brick/money": "^0.15.1",
|
||||||
"datomatic/laravel-enum-helper": "^2.0.0",
|
"datomatic/laravel-enum-helper": "^2.0.0",
|
||||||
"dedoc/scramble": "^0.13.43",
|
"dedoc/scramble": "^0.13.43",
|
||||||
"filament/filament": "^5.0",
|
"filament/filament": "^5.0",
|
||||||
"flowframe/laravel-trend": "^0.5.0",
|
"flowframe/laravel-trend": "^0.5.0",
|
||||||
"gotenberg/gotenberg-php": "^2.8",
|
"gotenberg/gotenberg-php": "^2.8",
|
||||||
"guzzlehttp/guzzle": "^7.2",
|
"guzzlehttp/guzzle": "^8.2",
|
||||||
"inertiajs/inertia-laravel": "^3.3.4",
|
"inertiajs/inertia-laravel": "^3.3.4",
|
||||||
"korridor/laravel-computed-attributes": "^3.1",
|
"korridor/laravel-computed-attributes": "^3.1",
|
||||||
"korridor/laravel-has-many-sync": "^3.1",
|
"korridor/laravel-has-many-sync": "^3.1",
|
||||||
"korridor/laravel-model-validation-rules": "^3.0",
|
"korridor/laravel-model-validation-rules": "^3.0",
|
||||||
"laravel/fortify": "^1.37",
|
"laravel/fortify": "^1.37",
|
||||||
"laravel/framework": "^12.19.3",
|
"laravel/framework": "^13.0",
|
||||||
"laravel/octane": "^2.3",
|
"laravel/octane": "^2.3",
|
||||||
"laravel/passport": "^13.0.5",
|
"laravel/passport": "^13.0.5",
|
||||||
"laravel/tinker": "^2.8",
|
"laravel/tinker": "^3.0",
|
||||||
"league/csv": "^9.16.0",
|
"league/csv": "^9.16.0",
|
||||||
"league/flysystem-aws-s3-v3": "^3.0",
|
"league/flysystem-aws-s3-v3": "^3.0",
|
||||||
"league/iso3166": "^4.3",
|
"league/iso3166": "^4.3",
|
||||||
|
|||||||
694
composer.lock
generated
694
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -24,9 +24,9 @@ return [
|
|||||||
| Application Name
|
| Application Name
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| This value is the name of your application. This value is used when the
|
| This value is the name of your application, which will be used when the
|
||||||
| framework needs to place the application's name in a notification or
|
| framework needs to place the application's name in a notification or
|
||||||
| any other location as required by the application or its packages.
|
| other UI elements where an application name needs to be displayed.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -110,8 +110,8 @@ return [
|
|||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Here you may specify the default timezone for your application, which
|
| Here you may specify the default timezone for your application, which
|
||||||
| will be used by the PHP date and date-time functions. We have gone
|
| will be used by the PHP date and date-time functions. The timezone
|
||||||
| ahead and set this to a sensible default for you out of the box.
|
| is set to "UTC" by default as it is suitable for most use cases.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -123,8 +123,8 @@ return [
|
|||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| The application locale determines the default locale that will be used
|
| The application locale determines the default locale that will be used
|
||||||
| by the translation service provider. You are free to set this value
|
| by Laravel's translation / localization methods. This option can be
|
||||||
| to any of the locales which will be supported by the application.
|
| set to any locale for which you plan to have translation strings.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -178,15 +178,21 @@ return [
|
|||||||
| Encryption Key
|
| Encryption Key
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| This key is used by the Illuminate encrypter service and should be set
|
| This key is utilized by Laravel's encryption services and should be set
|
||||||
| to a random, 32 character string, otherwise these encrypted strings
|
| to a random, 32 character string to ensure that all encrypted values
|
||||||
| will not be safe. Please do this before deploying an application!
|
| are secure. You should do this prior to deploying the application.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
'cipher' => 'AES-256-CBC',
|
||||||
|
|
||||||
'key' => env('APP_KEY'),
|
'key' => env('APP_KEY'),
|
||||||
|
|
||||||
'cipher' => 'AES-256-CBC',
|
'previous_keys' => [
|
||||||
|
...array_filter(
|
||||||
|
explode(',', (string) env('APP_PREVIOUS_KEYS', ''))
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|
||||||
'localization' => [
|
'localization' => [
|
||||||
'default_currency' => env('LOCALIZATION_DEFAULT_CURRENCY', 'EUR'),
|
'default_currency' => env('LOCALIZATION_DEFAULT_CURRENCY', 'EUR'),
|
||||||
@@ -206,13 +212,13 @@ return [
|
|||||||
| manage Laravel's "maintenance mode" status. The "cache" driver will
|
| manage Laravel's "maintenance mode" status. The "cache" driver will
|
||||||
| allow maintenance mode to be controlled across multiple machines.
|
| allow maintenance mode to be controlled across multiple machines.
|
||||||
|
|
|
|
||||||
| Supported drivers: "file", "cache"
|
| Supported drivers: "file", "cache", "array"
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'maintenance' => [
|
'maintenance' => [
|
||||||
'driver' => 'file',
|
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
|
||||||
// 'store' => 'redis',
|
'store' => env('APP_MAINTENANCE_STORE', 'database'),
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ return [
|
|||||||
| Authentication Defaults
|
| Authentication Defaults
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| This option controls the default authentication "guard" and password
|
| This option defines the default authentication "guard" and password
|
||||||
| reset options for your application. You may change these defaults
|
| reset "broker" for your application. You may change these values
|
||||||
| as required, but they're a perfect start for most applications.
|
| as required, but they're a perfect start for most applications.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
@@ -28,11 +28,11 @@ return [
|
|||||||
|
|
|
|
||||||
| Next, you may define every authentication guard for your application.
|
| Next, you may define every authentication guard for your application.
|
||||||
| Of course, a great default configuration has been defined for you
|
| Of course, a great default configuration has been defined for you
|
||||||
| here which uses session storage and the Eloquent user provider.
|
| which utilizes session storage plus the Eloquent user provider.
|
||||||
|
|
|
|
||||||
| All authentication drivers have a user provider. This defines how the
|
| All authentication guards have a user provider, which defines how the
|
||||||
| users are actually retrieved out of your database or other storage
|
| users are actually retrieved out of your database or other storage
|
||||||
| mechanisms used by this application to persist your user's data.
|
| system used by the application. Typically, Eloquent is utilized.
|
||||||
|
|
|
|
||||||
| Supported: "session"
|
| Supported: "session"
|
||||||
|
|
|
|
||||||
@@ -55,12 +55,12 @@ return [
|
|||||||
| User Providers
|
| User Providers
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| All authentication drivers have a user provider. This defines how the
|
| All authentication guards have a user provider, which defines how the
|
||||||
| users are actually retrieved out of your database or other storage
|
| users are actually retrieved out of your database or other storage
|
||||||
| mechanisms used by this application to persist your user's data.
|
| system used by the application. Typically, Eloquent is utilized.
|
||||||
|
|
|
|
||||||
| If you have multiple user tables or models you may configure multiple
|
| If you have multiple user tables or models you may configure multiple
|
||||||
| sources which represent each model / table. These sources may then
|
| providers to represent the model / table. These providers may then
|
||||||
| be assigned to any extra authentication guards you have defined.
|
| be assigned to any extra authentication guards you have defined.
|
||||||
|
|
|
|
||||||
| Supported: "database", "eloquent"
|
| Supported: "database", "eloquent"
|
||||||
@@ -80,9 +80,9 @@ return [
|
|||||||
| Resetting Passwords
|
| Resetting Passwords
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| You may specify multiple password reset configurations if you have more
|
| These configuration options specify the behavior of Laravel's password
|
||||||
| than one user table or model in the application and you want to have
|
| reset functionality, including the table utilized for token storage
|
||||||
| separate password reset settings based on the specific user types.
|
| and the user provider that is invoked to actually retrieve users.
|
||||||
|
|
|
|
||||||
| The expiry time is the number of minutes that each reset token will be
|
| The expiry time is the number of minutes that each reset token will be
|
||||||
| considered valid. This security feature keeps tokens short-lived so
|
| considered valid. This security feature keeps tokens short-lived so
|
||||||
@@ -108,13 +108,13 @@ return [
|
|||||||
| Password Confirmation Timeout
|
| Password Confirmation Timeout
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Here you may define the amount of seconds before a password confirmation
|
| Here you may define the number of seconds before a password confirmation
|
||||||
| times out and the user is prompted to re-enter their password via the
|
| window expires and users are asked to re-enter their password via the
|
||||||
| confirmation screen. By default, the timeout lasts for three hours.
|
| confirmation screen. By default, the timeout lasts for three hours.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'password_timeout' => 10800,
|
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
|
||||||
|
|
||||||
'super_admins' => ! is_string(env('SUPER_ADMINS', null)) ? [] : explode(',', env('SUPER_ADMINS')),
|
'super_admins' => ! is_string(env('SUPER_ADMINS', null)) ? [] : explode(',', env('SUPER_ADMINS')),
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ return [
|
|||||||
| Default Cache Store
|
| Default Cache Store
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| This option controls the default cache connection that gets used while
|
| This option controls the default cache store that will be used by the
|
||||||
| using this caching library. This connection is used when another is
|
| framework. This connection is utilized if another isn't explicitly
|
||||||
| not explicitly specified when executing a given caching function.
|
| specified when running a cache operation inside the application.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -28,8 +28,9 @@ return [
|
|||||||
| well as their drivers. You may even define multiple stores for the
|
| well as their drivers. You may even define multiple stores for the
|
||||||
| same cache driver to group types of items stored in your caches.
|
| same cache driver to group types of items stored in your caches.
|
||||||
|
|
|
|
||||||
| Supported drivers: "apc", "array", "database", "file",
|
| Supported drivers: "array", "database", "file", "memcached",
|
||||||
| "memcached", "redis", "dynamodb", "octane", "null"
|
| "redis", "dynamodb", "storage", "octane",
|
||||||
|
| "session", "failover", "null"
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -46,9 +47,10 @@ return [
|
|||||||
|
|
||||||
'database' => [
|
'database' => [
|
||||||
'driver' => 'database',
|
'driver' => 'database',
|
||||||
'table' => 'cache',
|
'connection' => env('DB_CACHE_CONNECTION'),
|
||||||
'connection' => null,
|
'table' => env('DB_CACHE_TABLE', 'cache'),
|
||||||
'lock_connection' => null,
|
'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
|
||||||
|
'lock_table' => env('DB_CACHE_LOCK_TABLE'),
|
||||||
],
|
],
|
||||||
|
|
||||||
'file' => [
|
'file' => [
|
||||||
@@ -57,6 +59,12 @@ return [
|
|||||||
'lock_path' => storage_path('framework/cache/data'),
|
'lock_path' => storage_path('framework/cache/data'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'storage' => [
|
||||||
|
'driver' => 'storage',
|
||||||
|
'disk' => env('CACHE_STORAGE_DISK'),
|
||||||
|
'path' => env('CACHE_STORAGE_PATH', 'framework/cache/data'),
|
||||||
|
],
|
||||||
|
|
||||||
'memcached' => [
|
'memcached' => [
|
||||||
'driver' => 'memcached',
|
'driver' => 'memcached',
|
||||||
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
|
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
|
||||||
@@ -78,8 +86,8 @@ return [
|
|||||||
|
|
||||||
'redis' => [
|
'redis' => [
|
||||||
'driver' => 'redis',
|
'driver' => 'redis',
|
||||||
'connection' => 'cache',
|
'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
|
||||||
'lock_connection' => 'default',
|
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
|
||||||
],
|
],
|
||||||
|
|
||||||
'dynamodb' => [
|
'dynamodb' => [
|
||||||
@@ -95,6 +103,14 @@ return [
|
|||||||
'driver' => 'octane',
|
'driver' => 'octane',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'failover' => [
|
||||||
|
'driver' => 'failover',
|
||||||
|
'stores' => [
|
||||||
|
'database',
|
||||||
|
'array',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -102,12 +118,24 @@ return [
|
|||||||
| Cache Key Prefix
|
| Cache Key Prefix
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| When utilizing the APC, database, memcached, Redis, or DynamoDB cache
|
| When utilizing the APC, database, memcached, Redis, and DynamoDB cache
|
||||||
| stores there might be other applications using the same cache. For
|
| stores, there might be other applications using the same cache. For
|
||||||
| that reason, you may prefix every cache key to avoid collisions.
|
| that reason, you may prefix every cache key to avoid collisions.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
|
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Serializable Classes
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value determines the classes that can be unserialized from cache
|
||||||
|
| storage. By default, no PHP classes will be unserialized from your
|
||||||
|
| cache to prevent gadget chain attacks if your APP_KEY is leaked.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'serializable_classes' => false,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -12,26 +12,22 @@ return [
|
|||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Here you may specify which of the database connections below you wish
|
| Here you may specify which of the database connections below you wish
|
||||||
| to use as your default connection for all database work. Of course
|
| to use as your default connection for database operations. This is
|
||||||
| you may use many connections at once using the Database library.
|
| the connection which will be utilized unless another connection
|
||||||
|
| is explicitly specified when you execute a query / statement.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'default' => env('DB_CONNECTION', 'mysql'),
|
'default' => env('DB_CONNECTION', 'pgsql'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Database Connections
|
| Database Connections
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Here are each of the database connections setup for your application.
|
| Below are all of the database connections defined for your application.
|
||||||
| Of course, examples of configuring each database platform that is
|
| An example configuration is provided for each database system which
|
||||||
| supported by Laravel is shown below to make development simple.
|
| is supported by Laravel. You're free to add / remove connections.
|
||||||
|
|
|
||||||
|
|
|
||||||
| All database work in Laravel is done through the PHP PDO facilities
|
|
||||||
| so make sure you have the driver for your particular database of
|
|
||||||
| choice installed on your machine before you begin development.
|
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -39,50 +35,34 @@ return [
|
|||||||
|
|
||||||
'sqlite' => [
|
'sqlite' => [
|
||||||
'driver' => 'sqlite',
|
'driver' => 'sqlite',
|
||||||
'url' => env('DATABASE_URL'),
|
'url' => env('DB_URL'),
|
||||||
'database' => env('DB_DATABASE', database_path('database.sqlite')),
|
'database' => env('DB_DATABASE', database_path('database.sqlite')),
|
||||||
'prefix' => '',
|
'prefix' => '',
|
||||||
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
|
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
|
||||||
],
|
'busy_timeout' => null,
|
||||||
|
'journal_mode' => null,
|
||||||
'mysql' => [
|
'synchronous' => null,
|
||||||
'driver' => 'mysql',
|
'transaction_mode' => 'DEFERRED',
|
||||||
'url' => env('DATABASE_URL'),
|
|
||||||
'host' => env('DB_HOST', '127.0.0.1'),
|
|
||||||
'port' => env('DB_PORT', '3306'),
|
|
||||||
'database' => env('DB_DATABASE', 'forge'),
|
|
||||||
'username' => env('DB_USERNAME', 'forge'),
|
|
||||||
'password' => env('DB_PASSWORD', ''),
|
|
||||||
'unix_socket' => env('DB_SOCKET', ''),
|
|
||||||
'charset' => 'utf8mb4',
|
|
||||||
'collation' => 'utf8mb4_unicode_ci',
|
|
||||||
'prefix' => '',
|
|
||||||
'prefix_indexes' => true,
|
|
||||||
'strict' => true,
|
|
||||||
'engine' => null,
|
|
||||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
|
||||||
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
|
||||||
]) : [],
|
|
||||||
],
|
],
|
||||||
|
|
||||||
'pgsql' => [
|
'pgsql' => [
|
||||||
'driver' => 'pgsql',
|
'driver' => 'pgsql',
|
||||||
'url' => env('DATABASE_URL'),
|
'url' => env('DB_URL'),
|
||||||
'host' => env('DB_HOST', '127.0.0.1'),
|
'host' => env('DB_HOST', '127.0.0.1'),
|
||||||
'port' => env('DB_PORT', '5432'),
|
'port' => env('DB_PORT', '5432'),
|
||||||
'database' => env('DB_DATABASE', 'forge'),
|
'database' => env('DB_DATABASE', 'forge'),
|
||||||
'username' => env('DB_USERNAME', 'forge'),
|
'username' => env('DB_USERNAME', 'forge'),
|
||||||
'password' => env('DB_PASSWORD', ''),
|
'password' => env('DB_PASSWORD', ''),
|
||||||
'charset' => 'utf8',
|
'charset' => env('DB_CHARSET', 'utf8'),
|
||||||
'prefix' => '',
|
'prefix' => '',
|
||||||
'prefix_indexes' => true,
|
'prefix_indexes' => true,
|
||||||
'search_path' => 'public',
|
'search_path' => 'public',
|
||||||
'sslmode' => 'prefer',
|
'sslmode' => env('DB_SSLMODE', 'prefer'),
|
||||||
],
|
],
|
||||||
|
|
||||||
'pgsql_test' => [
|
'pgsql_test' => [
|
||||||
'driver' => 'pgsql',
|
'driver' => 'pgsql',
|
||||||
'url' => env('DATABASE_URL'),
|
'url' => env('DB_URL'),
|
||||||
'host' => env('DB_TEST_HOST', '127.0.0.1'),
|
'host' => env('DB_TEST_HOST', '127.0.0.1'),
|
||||||
'port' => env('DB_TEST_PORT', '5432'),
|
'port' => env('DB_TEST_PORT', '5432'),
|
||||||
'database' => env('DB_TEST_DATABASE', 'forge'),
|
'database' => env('DB_TEST_DATABASE', 'forge'),
|
||||||
@@ -92,18 +72,18 @@ return [
|
|||||||
'prefix' => '',
|
'prefix' => '',
|
||||||
'prefix_indexes' => true,
|
'prefix_indexes' => true,
|
||||||
'search_path' => 'public',
|
'search_path' => 'public',
|
||||||
'sslmode' => 'prefer',
|
'sslmode' => env('DB_SSLMODE', 'prefer'),
|
||||||
],
|
],
|
||||||
|
|
||||||
'sqlsrv' => [
|
'sqlsrv' => [
|
||||||
'driver' => 'sqlsrv',
|
'driver' => 'sqlsrv',
|
||||||
'url' => env('DATABASE_URL'),
|
'url' => env('DB_URL'),
|
||||||
'host' => env('DB_HOST', 'localhost'),
|
'host' => env('DB_HOST', 'localhost'),
|
||||||
'port' => env('DB_PORT', '1433'),
|
'port' => env('DB_PORT', '1433'),
|
||||||
'database' => env('DB_DATABASE', 'forge'),
|
'database' => env('DB_DATABASE', 'laravel'),
|
||||||
'username' => env('DB_USERNAME', 'forge'),
|
'username' => env('DB_USERNAME', 'root'),
|
||||||
'password' => env('DB_PASSWORD', ''),
|
'password' => env('DB_PASSWORD', ''),
|
||||||
'charset' => 'utf8',
|
'charset' => env('DB_CHARSET', 'utf8'),
|
||||||
'prefix' => '',
|
'prefix' => '',
|
||||||
'prefix_indexes' => true,
|
'prefix_indexes' => true,
|
||||||
// 'encrypt' => env('DB_ENCRYPT', 'yes'),
|
// 'encrypt' => env('DB_ENCRYPT', 'yes'),
|
||||||
@@ -119,11 +99,14 @@ return [
|
|||||||
|
|
|
|
||||||
| This table keeps track of all the migrations that have already run for
|
| This table keeps track of all the migrations that have already run for
|
||||||
| your application. Using this information, we can determine which of
|
| your application. Using this information, we can determine which of
|
||||||
| the migrations on disk haven't actually been run in the database.
|
| the migrations on disk haven't actually been run on the database.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'migrations' => 'migrations',
|
'migrations' => [
|
||||||
|
'table' => 'migrations',
|
||||||
|
'update_date_on_publish' => false,
|
||||||
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@@ -132,7 +115,7 @@ return [
|
|||||||
|
|
|
|
||||||
| Redis is an open source, fast, and advanced key-value store that also
|
| Redis is an open source, fast, and advanced key-value store that also
|
||||||
| provides a richer body of commands than a typical key-value system
|
| provides a richer body of commands than a typical key-value system
|
||||||
| such as APC or Memcached. Laravel makes it easy to dig right in.
|
| such as Memcached. You may define your connection settings here.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -143,6 +126,7 @@ return [
|
|||||||
'options' => [
|
'options' => [
|
||||||
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
||||||
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
|
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
|
||||||
|
'persistent' => env('REDIS_PERSISTENT', false),
|
||||||
],
|
],
|
||||||
|
|
||||||
'default' => [
|
'default' => [
|
||||||
@@ -152,6 +136,10 @@ return [
|
|||||||
'password' => env('REDIS_PASSWORD'),
|
'password' => env('REDIS_PASSWORD'),
|
||||||
'port' => env('REDIS_PORT', '6379'),
|
'port' => env('REDIS_PORT', '6379'),
|
||||||
'database' => env('REDIS_DB', '0'),
|
'database' => env('REDIS_DB', '0'),
|
||||||
|
'max_retries' => env('REDIS_MAX_RETRIES', 3),
|
||||||
|
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
|
||||||
|
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
|
||||||
|
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
|
||||||
],
|
],
|
||||||
|
|
||||||
'cache' => [
|
'cache' => [
|
||||||
@@ -161,6 +149,10 @@ return [
|
|||||||
'password' => env('REDIS_PASSWORD'),
|
'password' => env('REDIS_PASSWORD'),
|
||||||
'port' => env('REDIS_PORT', '6379'),
|
'port' => env('REDIS_PORT', '6379'),
|
||||||
'database' => env('REDIS_CACHE_DB', '1'),
|
'database' => env('REDIS_CACHE_DB', '1'),
|
||||||
|
'max_retries' => env('REDIS_MAX_RETRIES', 3),
|
||||||
|
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
|
||||||
|
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
|
||||||
|
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
|
||||||
],
|
],
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ return [
|
|||||||
|
|
|
|
||||||
| Here you may specify the default filesystem disk that should be used
|
| Here you may specify the default filesystem disk that should be used
|
||||||
| by the framework. The "local" disk, as well as a variety of cloud
|
| by the framework. The "local" disk, as well as a variety of cloud
|
||||||
| based disks are available to your application. Just store away!
|
| based disks are available to your application for file storage.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -26,11 +26,11 @@ return [
|
|||||||
| Filesystem Disks
|
| Filesystem Disks
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Here you may configure as many filesystem "disks" as you wish, and you
|
| Below you may configure as many filesystem disks as necessary, and you
|
||||||
| may even configure multiple disks of the same driver. Defaults have
|
| may even configure multiple disks for the same driver. Examples for
|
||||||
| been set up for each driver as an example of the required values.
|
| most supported storage drivers are configured here for reference.
|
||||||
|
|
|
|
||||||
| Supported Drivers: "local", "ftp", "sftp", "s3"
|
| Supported drivers: "local", "ftp", "sftp", "s3"
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -41,14 +41,16 @@ return [
|
|||||||
'root' => storage_path('app'),
|
'root' => storage_path('app'),
|
||||||
'serve' => true,
|
'serve' => true,
|
||||||
'throw' => true,
|
'throw' => true,
|
||||||
|
'report' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
'public' => [
|
'public' => [
|
||||||
'driver' => 'local',
|
'driver' => 'local',
|
||||||
'root' => storage_path('app/public'),
|
'root' => storage_path('app/public'),
|
||||||
'url' => env('APP_URL').'/storage',
|
'url' => rtrim(env('APP_URL', 'http://localhost'), '/').'/storage',
|
||||||
'visibility' => 'public',
|
'visibility' => 'public',
|
||||||
'throw' => true,
|
'throw' => true,
|
||||||
|
'report' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
's3' => [
|
's3' => [
|
||||||
@@ -62,6 +64,7 @@ return [
|
|||||||
'endpoint' => env('S3_ENDPOINT'),
|
'endpoint' => env('S3_ENDPOINT'),
|
||||||
'use_path_style_endpoint' => env('S3_USE_PATH_STYLE_ENDPOINT', false),
|
'use_path_style_endpoint' => env('S3_USE_PATH_STYLE_ENDPOINT', false),
|
||||||
'throw' => true,
|
'throw' => true,
|
||||||
|
'report' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
'testfiles' => [
|
'testfiles' => [
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ return [
|
|||||||
| Default Log Channel
|
| Default Log Channel
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| This option defines the default log channel that gets used when writing
|
| This option defines the default log channel that is utilized to write
|
||||||
| messages to the logs. The name specified in this option should match
|
| messages to your logs. The value provided here should match one of
|
||||||
| one of the channels defined in the "channels" configuration array.
|
| the channels present in the list of "channels" configured below.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -43,20 +43,20 @@ return [
|
|||||||
| Log Channels
|
| Log Channels
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Here you may configure the log channels for your application. Out of
|
| Here you may configure the log channels for your application. Laravel
|
||||||
| the box, Laravel uses the Monolog PHP logging library. This gives
|
| utilizes the Monolog PHP logging library, which includes a variety
|
||||||
| you a variety of powerful log handlers / formatters to utilize.
|
| of powerful log handlers and formatters that you're free to use.
|
||||||
|
|
|
|
||||||
| Available Drivers: "single", "daily", "slack", "syslog",
|
| Available drivers: "single", "daily", "monthly", "slack", "syslog",
|
||||||
| "errorlog", "monolog",
|
| "errorlog", "monolog", "custom", "stack"
|
||||||
| "custom", "stack"
|
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'channels' => [
|
'channels' => [
|
||||||
|
|
||||||
'stack' => [
|
'stack' => [
|
||||||
'driver' => 'stack',
|
'driver' => 'stack',
|
||||||
'channels' => ['single'],
|
'channels' => explode(',', (string) env('LOG_STACK', 'single')),
|
||||||
'ignore_exceptions' => false,
|
'ignore_exceptions' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -81,15 +81,23 @@ return [
|
|||||||
'driver' => 'daily',
|
'driver' => 'daily',
|
||||||
'path' => storage_path('logs/laravel.log'),
|
'path' => storage_path('logs/laravel.log'),
|
||||||
'level' => env('LOG_LEVEL', 'debug'),
|
'level' => env('LOG_LEVEL', 'debug'),
|
||||||
'days' => 14,
|
'max_files' => env('LOG_DAILY_DAYS', 14),
|
||||||
|
'replace_placeholders' => true,
|
||||||
|
],
|
||||||
|
|
||||||
|
'monthly' => [
|
||||||
|
'driver' => 'monthly',
|
||||||
|
'path' => storage_path('logs/laravel.log'),
|
||||||
|
'level' => env('LOG_LEVEL', 'debug'),
|
||||||
|
'max_files' => 3,
|
||||||
'replace_placeholders' => true,
|
'replace_placeholders' => true,
|
||||||
],
|
],
|
||||||
|
|
||||||
'slack' => [
|
'slack' => [
|
||||||
'driver' => 'slack',
|
'driver' => 'slack',
|
||||||
'url' => env('LOG_SLACK_WEBHOOK_URL'),
|
'url' => env('LOG_SLACK_WEBHOOK_URL'),
|
||||||
'username' => 'Laravel Log',
|
'username' => env('LOG_SLACK_USERNAME', env('APP_NAME', 'Laravel')),
|
||||||
'emoji' => ':boom:',
|
'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
|
||||||
'level' => env('LOG_LEVEL', 'critical'),
|
'level' => env('LOG_LEVEL', 'critical'),
|
||||||
'replace_placeholders' => true,
|
'replace_placeholders' => true,
|
||||||
],
|
],
|
||||||
@@ -116,17 +124,17 @@ return [
|
|||||||
'driver' => 'monolog',
|
'driver' => 'monolog',
|
||||||
'level' => env('LOG_LEVEL', 'debug'),
|
'level' => env('LOG_LEVEL', 'debug'),
|
||||||
'handler' => StreamHandler::class,
|
'handler' => StreamHandler::class,
|
||||||
'formatter' => env('LOG_STDERR_FORMATTER'),
|
'handler_with' => [
|
||||||
'with' => [
|
|
||||||
'stream' => 'php://stderr',
|
'stream' => 'php://stderr',
|
||||||
],
|
],
|
||||||
|
'formatter' => env('LOG_STDERR_FORMATTER'),
|
||||||
'processors' => [PsrLogMessageProcessor::class],
|
'processors' => [PsrLogMessageProcessor::class],
|
||||||
],
|
],
|
||||||
|
|
||||||
'syslog' => [
|
'syslog' => [
|
||||||
'driver' => 'syslog',
|
'driver' => 'syslog',
|
||||||
'level' => env('LOG_LEVEL', 'debug'),
|
'level' => env('LOG_LEVEL', 'debug'),
|
||||||
'facility' => LOG_USER,
|
'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
|
||||||
'replace_placeholders' => true,
|
'replace_placeholders' => true,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ return [
|
|||||||
| Default Mailer
|
| Default Mailer
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| This option controls the default mailer that is used to send any email
|
| This option controls the default mailer that is used to send all email
|
||||||
| messages sent by your application. Alternative mailers may be setup
|
| messages unless another mailer is explicitly specified when sending
|
||||||
| and used as needed; however, this mailer will be used by default.
|
| the message. All additional mailers can be configured within the
|
||||||
|
| "mailers" array. Examples of each type of mailer are provided.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -26,26 +27,28 @@ return [
|
|||||||
| their respective settings. Several examples have been configured for
|
| their respective settings. Several examples have been configured for
|
||||||
| you and you are free to add your own as your application requires.
|
| you and you are free to add your own as your application requires.
|
||||||
|
|
|
|
||||||
| Laravel supports a variety of mail "transport" drivers to be used while
|
| Laravel supports a variety of mail "transport" drivers that can be used
|
||||||
| sending an e-mail. You will specify which one you are using for your
|
| when delivering an email. You may specify which one you're using for
|
||||||
| mailers below. You are free to add additional mailers as required.
|
| your mailers below. You may also add additional mailers if needed.
|
||||||
|
|
|
|
||||||
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
|
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
|
||||||
| "postmark", "log", "array", "failover", "roundrobin"
|
| "postmark", "resend", "log", "array",
|
||||||
|
| "failover", "roundrobin"
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'mailers' => [
|
'mailers' => [
|
||||||
'smtp' => [
|
'smtp' => [
|
||||||
'transport' => 'smtp',
|
'transport' => 'smtp',
|
||||||
|
'scheme' => env('MAIL_SCHEME'),
|
||||||
'url' => env('MAIL_URL'),
|
'url' => env('MAIL_URL'),
|
||||||
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
|
'host' => env('MAIL_HOST', '127.0.0.1'),
|
||||||
'port' => env('MAIL_PORT', 587),
|
'port' => env('MAIL_PORT', 2525),
|
||||||
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
|
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
|
||||||
'username' => env('MAIL_USERNAME'),
|
'username' => env('MAIL_USERNAME'),
|
||||||
'password' => env('MAIL_PASSWORD'),
|
'password' => env('MAIL_PASSWORD'),
|
||||||
'timeout' => null,
|
'timeout' => null,
|
||||||
'local_domain' => env('MAIL_EHLO_DOMAIN'),
|
'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
|
||||||
],
|
],
|
||||||
|
|
||||||
'ses' => [
|
'ses' => [
|
||||||
@@ -54,17 +57,14 @@ return [
|
|||||||
|
|
||||||
'postmark' => [
|
'postmark' => [
|
||||||
'transport' => 'postmark',
|
'transport' => 'postmark',
|
||||||
// 'message_stream_id' => null,
|
// 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
|
||||||
// 'client' => [
|
// 'client' => [
|
||||||
// 'timeout' => 5,
|
// 'timeout' => 5,
|
||||||
// ],
|
// ],
|
||||||
],
|
],
|
||||||
|
|
||||||
'mailgun' => [
|
'resend' => [
|
||||||
'transport' => 'mailgun',
|
'transport' => 'resend',
|
||||||
// 'client' => [
|
|
||||||
// 'timeout' => 5,
|
|
||||||
// ],
|
|
||||||
],
|
],
|
||||||
|
|
||||||
'sendmail' => [
|
'sendmail' => [
|
||||||
@@ -87,6 +87,7 @@ return [
|
|||||||
'smtp',
|
'smtp',
|
||||||
'log',
|
'log',
|
||||||
],
|
],
|
||||||
|
'retry_after' => 60,
|
||||||
],
|
],
|
||||||
|
|
||||||
'roundrobin' => [
|
'roundrobin' => [
|
||||||
@@ -95,7 +96,9 @@ return [
|
|||||||
'ses',
|
'ses',
|
||||||
'postmark',
|
'postmark',
|
||||||
],
|
],
|
||||||
|
'retry_after' => 60,
|
||||||
],
|
],
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -103,15 +106,15 @@ return [
|
|||||||
| Global "From" Address
|
| Global "From" Address
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| You may wish for all e-mails sent by your application to be sent from
|
| You may wish for all emails sent by your application to be sent from
|
||||||
| the same address. Here, you may specify a name and address that is
|
| the same address. Here you may specify a name and address that is
|
||||||
| used globally for all e-mails that are sent by your application.
|
| used globally for all emails that are sent by your application.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'from' => [
|
'from' => [
|
||||||
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
|
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
|
||||||
'name' => env('MAIL_FROM_NAME', 'Example'),
|
'name' => env('MAIL_FROM_NAME', env('APP_NAME', 'Laravel')),
|
||||||
],
|
],
|
||||||
|
|
||||||
'reply_to' => [
|
'reply_to' => [
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ return [
|
|||||||
| Default Queue Connection Name
|
| Default Queue Connection Name
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Laravel's queue API supports an assortment of back-ends via a single
|
| Laravel's queue supports a variety of backends via a single, unified
|
||||||
| API, giving you convenient access to each back-end using the same
|
| API, giving you convenient access to each backend using identical
|
||||||
| syntax for every one. Here you may define a default connection.
|
| syntax for each. The default queue connection is defined below.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -22,11 +22,12 @@ return [
|
|||||||
| Queue Connections
|
| Queue Connections
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Here you may configure the connection information for each server that
|
| Here you may configure the connection options for every queue backend
|
||||||
| is used by your application. A default configuration has been added
|
| used by your application. An example configuration is provided for
|
||||||
| for each back-end shipped with Laravel. You are free to add more.
|
| each backend supported by Laravel. You're also free to add more.
|
||||||
|
|
|
|
||||||
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
|
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis",
|
||||||
|
| "deferred", "background", "failover", "null"
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -38,17 +39,18 @@ return [
|
|||||||
|
|
||||||
'database' => [
|
'database' => [
|
||||||
'driver' => 'database',
|
'driver' => 'database',
|
||||||
'table' => 'jobs',
|
'connection' => env('DB_QUEUE_CONNECTION'),
|
||||||
'queue' => 'default',
|
'table' => env('DB_QUEUE_TABLE', 'jobs'),
|
||||||
'retry_after' => 90,
|
'queue' => env('DB_QUEUE', 'default'),
|
||||||
|
'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),
|
||||||
'after_commit' => false,
|
'after_commit' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
'beanstalkd' => [
|
'beanstalkd' => [
|
||||||
'driver' => 'beanstalkd',
|
'driver' => 'beanstalkd',
|
||||||
'host' => 'localhost',
|
'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'),
|
||||||
'queue' => 'default',
|
'queue' => env('BEANSTALKD_QUEUE', 'default'),
|
||||||
'retry_after' => 90,
|
'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),
|
||||||
'block_for' => 0,
|
'block_for' => 0,
|
||||||
'after_commit' => false,
|
'after_commit' => false,
|
||||||
],
|
],
|
||||||
@@ -66,13 +68,29 @@ return [
|
|||||||
|
|
||||||
'redis' => [
|
'redis' => [
|
||||||
'driver' => 'redis',
|
'driver' => 'redis',
|
||||||
'connection' => 'default',
|
'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
|
||||||
'queue' => env('REDIS_QUEUE', 'default'),
|
'queue' => env('REDIS_QUEUE', 'default'),
|
||||||
'retry_after' => 90,
|
'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
|
||||||
'block_for' => null,
|
'block_for' => null,
|
||||||
'after_commit' => false,
|
'after_commit' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'deferred' => [
|
||||||
|
'driver' => 'deferred',
|
||||||
|
],
|
||||||
|
|
||||||
|
'background' => [
|
||||||
|
'driver' => 'background',
|
||||||
|
],
|
||||||
|
|
||||||
|
'failover' => [
|
||||||
|
'driver' => 'failover',
|
||||||
|
'connections' => [
|
||||||
|
'database',
|
||||||
|
'deferred',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -87,7 +105,7 @@ return [
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
'batching' => [
|
'batching' => [
|
||||||
'database' => env('DB_CONNECTION', 'mysql'),
|
'database' => env('DB_CONNECTION', 'sqlite'),
|
||||||
'table' => 'job_batches',
|
'table' => 'job_batches',
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -97,14 +115,16 @@ return [
|
|||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| These options configure the behavior of failed queue job logging so you
|
| These options configure the behavior of failed queue job logging so you
|
||||||
| can control which database and table are used to store the jobs that
|
| can control how and where failed jobs are stored. Laravel ships with
|
||||||
| have failed. You may change them to any database / table you wish.
|
| support for storing failed jobs in a simple file or in a database.
|
||||||
|
|
|
||||||
|
| Supported drivers: "database-uuids", "dynamodb", "file", "null"
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'failed' => [
|
'failed' => [
|
||||||
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
|
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
|
||||||
'database' => env('DB_CONNECTION', 'mysql'),
|
'database' => env('DB_CONNECTION', 'sqlite'),
|
||||||
'table' => 'failed_jobs',
|
'table' => 'failed_jobs',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ return [
|
|||||||
| Default Session Driver
|
| Default Session Driver
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| This option controls the default session "driver" that will be used on
|
| This option determines the default session driver that is utilized for
|
||||||
| requests. By default, we will use the lightweight native driver but
|
| incoming requests. Laravel supports a variety of storage options to
|
||||||
| you may specify any of the other wonderful drivers provided here.
|
| persist session data. Database storage is a great default choice.
|
||||||
|
|
|
|
||||||
| Supported: "file", "cookie", "database", "apc",
|
| Supported: "file", "cookie", "database", "memcached",
|
||||||
| "memcached", "redis", "dynamodb", "array"
|
| "redis", "dynamodb", "array"
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -27,13 +27,14 @@ return [
|
|||||||
|
|
|
|
||||||
| Here you may specify the number of minutes that you wish the session
|
| Here you may specify the number of minutes that you wish the session
|
||||||
| to be allowed to remain idle before it expires. If you want them
|
| to be allowed to remain idle before it expires. If you want them
|
||||||
| to immediately expire on the browser closing, set that option.
|
| to expire immediately when the browser is closed then you may
|
||||||
|
| indicate that via the expire_on_close configuration option.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'lifetime' => env('SESSION_LIFETIME', 120),
|
'lifetime' => (int) env('SESSION_LIFETIME', 120),
|
||||||
|
|
||||||
'expire_on_close' => false,
|
'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@@ -79,24 +80,24 @@ return [
|
|||||||
| Session Database Table
|
| Session Database Table
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| When using the "database" session driver, you may specify the table we
|
| When using the "database" session driver, you may specify the table to
|
||||||
| should use to manage the sessions. Of course, a sensible default is
|
| be used to store sessions. Of course, a sensible default is defined
|
||||||
| provided for you; however, you are free to change this as needed.
|
| for you; however, you're welcome to change this to another table.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'table' => 'sessions',
|
'table' => env('SESSION_TABLE', 'sessions'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Session Cache Store
|
| Session Cache Store
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| While using one of the framework's cache driven session backends you may
|
| When using one of the framework's cache driven session backends, you may
|
||||||
| list a cache store that should be used for these sessions. This value
|
| define the cache store which should be used to store the session data
|
||||||
| must match with one of the application's configured cache "stores".
|
| between requests. This must match one of your defined cache stores.
|
||||||
|
|
|
|
||||||
| Affects: "apc", "dynamodb", "memcached", "redis"
|
| Affects: "dynamodb", "memcached", "redis"
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -120,9 +121,9 @@ return [
|
|||||||
| Session Cookie Name
|
| Session Cookie Name
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Here you may change the name of the cookie used to identify a session
|
| Here you may change the name of the session cookie that is created by
|
||||||
| instance by ID. The name specified here will get used every time a
|
| the framework. Typically, you should not need to change this value
|
||||||
| new session cookie is created by the framework for every driver.
|
| since doing so does not grant a meaningful security improvement.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -138,20 +139,20 @@ return [
|
|||||||
|
|
|
|
||||||
| The session cookie path determines the path for which the cookie will
|
| The session cookie path determines the path for which the cookie will
|
||||||
| be regarded as available. Typically, this will be the root path of
|
| be regarded as available. Typically, this will be the root path of
|
||||||
| your application but you are free to change this when necessary.
|
| your application, but you're free to change this when necessary.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'path' => '/',
|
'path' => env('SESSION_PATH', '/'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Session Cookie Domain
|
| Session Cookie Domain
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Here you may change the domain of the cookie used to identify a session
|
| This value determines the domain and subdomains the session cookie is
|
||||||
| in your application. This will determine which domains the cookie is
|
| available to. By default, the cookie will be available to the root
|
||||||
| available to in your application. A sensible default has been set.
|
| domain without subdomains. Typically, this shouldn't be changed.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -177,11 +178,11 @@ return [
|
|||||||
|
|
|
|
||||||
| Setting this value to true will prevent JavaScript from accessing the
|
| Setting this value to true will prevent JavaScript from accessing the
|
||||||
| value of the cookie and the cookie will only be accessible through
|
| value of the cookie and the cookie will only be accessible through
|
||||||
| the HTTP protocol. You are free to modify this option if needed.
|
| the HTTP protocol. It's unlikely you should disable this option.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'http_only' => true,
|
'http_only' => env('SESSION_HTTP_ONLY', true),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@@ -190,13 +191,15 @@ return [
|
|||||||
|
|
|
|
||||||
| This option determines how your cookies behave when cross-site requests
|
| This option determines how your cookies behave when cross-site requests
|
||||||
| take place, and can be used to mitigate CSRF attacks. By default, we
|
| take place, and can be used to mitigate CSRF attacks. By default, we
|
||||||
| will set this value to "lax" since this is a secure default value.
|
| will set this value to "lax" to permit secure cross-site requests.
|
||||||
|
|
|
||||||
|
| See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
|
||||||
|
|
|
|
||||||
| Supported: "lax", "strict", "none", null
|
| Supported: "lax", "strict", "none", null
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'same_site' => 'lax',
|
'same_site' => env('SESSION_SAME_SITE', 'lax'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@@ -209,6 +212,22 @@ return [
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'partitioned' => false,
|
'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Session Serialization
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value controls the serialization strategy for session data, which
|
||||||
|
| is JSON by default. Setting this to "php" allows the storage of PHP
|
||||||
|
| objects in the session but can make an application vulnerable to
|
||||||
|
| "gadget chain" serialization attacks if the APP_KEY is leaked.
|
||||||
|
|
|
||||||
|
| Supported: "json", "php"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'serialization' => 'php',
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export async function getPasswordResetUrl(
|
|||||||
for (let attempt = 0; attempt < 5; attempt++) {
|
for (let attempt = 0; attempt < 5; attempt++) {
|
||||||
searchResult = await searchEmails(
|
searchResult = await searchEmails(
|
||||||
request,
|
request,
|
||||||
`to:${encodeURIComponent(recipientEmail)} subject:"Reset Password"`
|
`to:${encodeURIComponent(recipientEmail)} subject:"Reset your password"`
|
||||||
);
|
);
|
||||||
if (searchResult.messages.length > 0) break;
|
if (searchResult.messages.length > 0) break;
|
||||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"Billing": {
|
"Billing": {
|
||||||
"repository": "solidtime-io/extension-billing",
|
"repository": "solidtime-io/extension-billing",
|
||||||
"ref": "feature/composer-update"
|
"ref": "v0.0.7"
|
||||||
},
|
},
|
||||||
"Services": {
|
"Services": {
|
||||||
"repository": "solidtime-io/extension-services",
|
"repository": "solidtime-io/extension-services",
|
||||||
"ref": "feature/composer-update"
|
"ref": "v0.0.3"
|
||||||
},
|
},
|
||||||
"Invoicing": {
|
"Invoicing": {
|
||||||
"repository": "solidtime-io/extension-invoicing",
|
"repository": "solidtime-io/extension-invoicing",
|
||||||
"ref": "feature/composer-update"
|
"ref": "v0.0.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||||
<env name="CACHE_DRIVER" value="array"/>
|
<env name="CACHE_DRIVER" value="array"/>
|
||||||
<env name="DB_CONNECTION" value="pgsql_test"/>
|
<env name="DB_CONNECTION" value="pgsql_test"/>
|
||||||
|
<env name="FILESYSTEM_DISK" value="local"/>
|
||||||
<env name="MAIL_MAILER" value="array"/>
|
<env name="MAIL_MAILER" value="array"/>
|
||||||
<env name="PULSE_ENABLED" value="false"/>
|
<env name="PULSE_ENABLED" value="false"/>
|
||||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Closure;
|
|||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Testing\TestResponse;
|
use Illuminate\Testing\TestResponse;
|
||||||
|
use Mockery;
|
||||||
use Tests\TestCaseWithDatabase;
|
use Tests\TestCaseWithDatabase;
|
||||||
|
|
||||||
class ApiEndpointTestAbstract extends TestCaseWithDatabase
|
class ApiEndpointTestAbstract extends TestCaseWithDatabase
|
||||||
@@ -21,21 +22,23 @@ class ApiEndpointTestAbstract extends TestCaseWithDatabase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces the temporary URL builder of the private disk to capture the options
|
* Captures the options passed to temporaryUrl on the private disk.
|
||||||
* passed to temporaryUrl. Returns a closure that yields the captured options.
|
|
||||||
*
|
*
|
||||||
* @return Closure(): (array<string, mixed>|null)
|
* @return Closure(): (array<string, mixed>|null)
|
||||||
*/
|
*/
|
||||||
protected function captureTemporaryUrlOptions(): Closure
|
protected function captureTemporaryUrlOptions(): Closure
|
||||||
{
|
{
|
||||||
$captured = null;
|
$captured = null;
|
||||||
Storage::disk(config('filesystems.private'))->buildTemporaryUrlsUsing(
|
$diskName = config('filesystems.private');
|
||||||
|
$disk = Mockery::mock(Storage::disk($diskName))->makePartial();
|
||||||
|
$disk->shouldReceive('temporaryUrl')->andReturnUsing(
|
||||||
function (string $path, DateTimeInterface $expiration, array $options) use (&$captured): string {
|
function (string $path, DateTimeInterface $expiration, array $options) use (&$captured): string {
|
||||||
$captured = $options;
|
$captured = $options;
|
||||||
|
|
||||||
return 'https://storage.fake/'.$path;
|
return 'https://storage.fake/'.$path;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
Storage::set($diskName, $disk);
|
||||||
|
|
||||||
return function () use (&$captured): ?array {
|
return function () use (&$captured): ?array {
|
||||||
return $captured;
|
return $captured;
|
||||||
|
|||||||
@@ -544,6 +544,68 @@ class OrganizationResourceTest extends FilamentTestCase
|
|||||||
$this->assertSame(Role::Owner->value, $ownerMember->refresh()->role);
|
$this->assertSame(Role::Owner->value, $ownerMember->refresh()->role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_edit_related_user_does_not_change_role_if_role_is_unchanged_for_owner(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$owner = User::factory()->create();
|
||||||
|
$organization = Organization::factory()->withOwner($owner)->create();
|
||||||
|
$ownerMember = Member::factory()->forOrganization($organization)->forUser($owner)->role(Role::Owner)->billableRate(1000)->create();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$response = Livewire::test(OrganizationResource\RelationManagers\UsersRelationManager::class, [
|
||||||
|
'ownerRecord' => $organization,
|
||||||
|
'pageClass' => OrganizationResource\Pages\EditOrganization::class,
|
||||||
|
])->callTableAction('edit', $owner, data: [
|
||||||
|
'role' => Role::Owner->value,
|
||||||
|
'billable_rate' => 2000,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$response->assertSuccessful();
|
||||||
|
$response->assertHasNoTableActionErrors();
|
||||||
|
$response->assertNotNotified(
|
||||||
|
Notification::make()
|
||||||
|
->danger()
|
||||||
|
->title('Update failed')
|
||||||
|
->body(__('exceptions.api.organization_needs_at_least_one_owner'))
|
||||||
|
->persistent()
|
||||||
|
);
|
||||||
|
$ownerMember->refresh();
|
||||||
|
$this->assertSame(Role::Owner->value, $ownerMember->role);
|
||||||
|
$this->assertSame(2000, $ownerMember->billable_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_edit_related_user_does_not_change_role_if_role_is_unchanged_for_placeholder(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$organization = Organization::factory()->create();
|
||||||
|
$user = User::factory()->placeholder()->create();
|
||||||
|
$member = Member::factory()->forOrganization($organization)->forUser($user)->role(Role::Placeholder)->billableRate(1000)->create();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$response = Livewire::test(OrganizationResource\RelationManagers\UsersRelationManager::class, [
|
||||||
|
'ownerRecord' => $organization,
|
||||||
|
'pageClass' => OrganizationResource\Pages\EditOrganization::class,
|
||||||
|
])->callTableAction('edit', $user, data: [
|
||||||
|
'role' => Role::Placeholder->value,
|
||||||
|
'billable_rate' => 2000,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$response->assertSuccessful();
|
||||||
|
$response->assertHasNoTableActionErrors();
|
||||||
|
$response->assertNotNotified(
|
||||||
|
Notification::make()
|
||||||
|
->danger()
|
||||||
|
->title('Update failed')
|
||||||
|
->body(__('exceptions.api.changing_role_of_placeholder_is_not_allowed'))
|
||||||
|
->persistent()
|
||||||
|
);
|
||||||
|
$member->refresh();
|
||||||
|
$this->assertSame(Role::Placeholder->value, $member->role);
|
||||||
|
$this->assertSame(2000, $member->billable_rate);
|
||||||
|
}
|
||||||
|
|
||||||
public function test_can_detach_related_user_from_organization(): void
|
public function test_can_detach_related_user_from_organization(): void
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|||||||
@@ -286,6 +286,62 @@ class UserResourceTest extends FilamentTestCase
|
|||||||
$this->assertSame(Role::Owner->value, $ownerMember->refresh()->role);
|
$this->assertSame(Role::Owner->value, $ownerMember->refresh()->role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_edit_related_organization_does_not_change_role_if_role_is_unchanged_for_owner(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$owner = User::factory()->create();
|
||||||
|
$organization = Organization::factory()->withOwner($owner)->create();
|
||||||
|
$ownerMember = Member::factory()->forOrganization($organization)->forUser($owner)->role(Role::Owner)->create();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$response = Livewire::test(UserResource\RelationManagers\OrganizationsRelationManager::class, [
|
||||||
|
'ownerRecord' => $owner,
|
||||||
|
'pageClass' => UserResource\Pages\EditUser::class,
|
||||||
|
])->callTableAction('edit', $organization, data: [
|
||||||
|
'role' => Role::Owner->value,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$response->assertSuccessful();
|
||||||
|
$response->assertHasNoTableActionErrors();
|
||||||
|
$response->assertNotNotified(
|
||||||
|
Notification::make()
|
||||||
|
->danger()
|
||||||
|
->title('Update failed')
|
||||||
|
->body(__('exceptions.api.organization_needs_at_least_one_owner'))
|
||||||
|
->persistent()
|
||||||
|
);
|
||||||
|
$this->assertSame(Role::Owner->value, $ownerMember->refresh()->role);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_edit_related_organization_does_not_change_role_if_role_is_unchanged_for_placeholder(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$user = User::factory()->placeholder()->create();
|
||||||
|
$organization = Organization::factory()->create();
|
||||||
|
$member = Member::factory()->forOrganization($organization)->forUser($user)->role(Role::Placeholder)->create();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$response = Livewire::test(UserResource\RelationManagers\OrganizationsRelationManager::class, [
|
||||||
|
'ownerRecord' => $user,
|
||||||
|
'pageClass' => UserResource\Pages\EditUser::class,
|
||||||
|
])->callTableAction('edit', $organization, data: [
|
||||||
|
'role' => Role::Placeholder->value,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$response->assertSuccessful();
|
||||||
|
$response->assertHasNoTableActionErrors();
|
||||||
|
$response->assertNotNotified(
|
||||||
|
Notification::make()
|
||||||
|
->danger()
|
||||||
|
->title('Update failed')
|
||||||
|
->body(__('exceptions.api.changing_role_of_placeholder_is_not_allowed'))
|
||||||
|
->persistent()
|
||||||
|
);
|
||||||
|
$this->assertSame(Role::Placeholder->value, $member->refresh()->role);
|
||||||
|
}
|
||||||
|
|
||||||
public function test_can_detach_related_organization_from_user(): void
|
public function test_can_detach_related_organization_from_user(): void
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ class LocalizationServiceTest extends TestCaseWithDatabase
|
|||||||
// Arrange
|
// Arrange
|
||||||
$this->localizationService->setCurrencyFormat(CurrencyFormat::SymbolAfterWithSpace);
|
$this->localizationService->setCurrencyFormat(CurrencyFormat::SymbolAfterWithSpace);
|
||||||
$this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma);
|
$this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma);
|
||||||
$money = Money::of(1234567.89, Currency::of('EUR'));
|
$money = Money::of('1234567.89', Currency::of('EUR'));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
$formatted = $this->localizationService->formatCurrency($money);
|
$formatted = $this->localizationService->formatCurrency($money);
|
||||||
@@ -201,7 +201,7 @@ class LocalizationServiceTest extends TestCaseWithDatabase
|
|||||||
// Arrange
|
// Arrange
|
||||||
$this->localizationService->setCurrencyFormat(CurrencyFormat::SymbolBeforeWithSpace);
|
$this->localizationService->setCurrencyFormat(CurrencyFormat::SymbolBeforeWithSpace);
|
||||||
$this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma);
|
$this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma);
|
||||||
$money = Money::of(1234567.89, Currency::of('EUR'));
|
$money = Money::of('1234567.89', Currency::of('EUR'));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
$formatted = $this->localizationService->formatCurrency($money);
|
$formatted = $this->localizationService->formatCurrency($money);
|
||||||
@@ -215,7 +215,7 @@ class LocalizationServiceTest extends TestCaseWithDatabase
|
|||||||
// Arrange
|
// Arrange
|
||||||
$this->localizationService->setCurrencyFormat(CurrencyFormat::SymbolBefore);
|
$this->localizationService->setCurrencyFormat(CurrencyFormat::SymbolBefore);
|
||||||
$this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma);
|
$this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma);
|
||||||
$money = Money::of(1234567.89, Currency::of('EUR'));
|
$money = Money::of('1234567.89', Currency::of('EUR'));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
$formatted = $this->localizationService->formatCurrency($money);
|
$formatted = $this->localizationService->formatCurrency($money);
|
||||||
@@ -229,7 +229,7 @@ class LocalizationServiceTest extends TestCaseWithDatabase
|
|||||||
// Arrange
|
// Arrange
|
||||||
$this->localizationService->setCurrencyFormat(CurrencyFormat::SymbolAfter);
|
$this->localizationService->setCurrencyFormat(CurrencyFormat::SymbolAfter);
|
||||||
$this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma);
|
$this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma);
|
||||||
$money = Money::of(1234567.89, Currency::of('EUR'));
|
$money = Money::of('1234567.89', Currency::of('EUR'));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
$formatted = $this->localizationService->formatCurrency($money);
|
$formatted = $this->localizationService->formatCurrency($money);
|
||||||
@@ -243,7 +243,7 @@ class LocalizationServiceTest extends TestCaseWithDatabase
|
|||||||
// Arrange
|
// Arrange
|
||||||
$this->localizationService->setCurrencyFormat(CurrencyFormat::ISOCodeAfterWithSpace);
|
$this->localizationService->setCurrencyFormat(CurrencyFormat::ISOCodeAfterWithSpace);
|
||||||
$this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma);
|
$this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma);
|
||||||
$money = Money::of(1234567.89, Currency::of('EUR'));
|
$money = Money::of('1234567.89', Currency::of('EUR'));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
$formatted = $this->localizationService->formatCurrency($money);
|
$formatted = $this->localizationService->formatCurrency($money);
|
||||||
@@ -257,7 +257,7 @@ class LocalizationServiceTest extends TestCaseWithDatabase
|
|||||||
// Arrange
|
// Arrange
|
||||||
$this->localizationService->setCurrencyFormat(CurrencyFormat::ISOCodeBeforeWithSpace);
|
$this->localizationService->setCurrencyFormat(CurrencyFormat::ISOCodeBeforeWithSpace);
|
||||||
$this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma);
|
$this->localizationService->setNumberFormat(NumberFormat::ThousandsSpaceDecimalComma);
|
||||||
$money = Money::of(1234567.89, Currency::of('EUR'));
|
$money = Money::of('1234567.89', Currency::of('EUR'));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
$formatted = $this->localizationService->formatCurrency($money);
|
$formatted = $this->localizationService->formatCurrency($money);
|
||||||
|
|||||||
Reference in New Issue
Block a user