diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index e6b9960..4c1dd5a 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -20,7 +20,7 @@ protected function schedule(Schedule $schedule): void */ protected function commands(): void { - $this->load(__DIR__.'/Commands'); + $this->load(__DIR__ . '/Commands'); require base_path('routes/console.php'); } diff --git a/app/Enums/ExpenseCategory.php b/app/Enums/ExpenseCategory.php index f5b3d1a..ec76a0b 100644 --- a/app/Enums/ExpenseCategory.php +++ b/app/Enums/ExpenseCategory.php @@ -26,7 +26,7 @@ public function getLabel(): ?string }; } - public function getColor(): string | array | null + public function getColor(): string|array|null { return match ($this) { self::Vat => 'teal', diff --git a/app/Enums/LanguageCode.php b/app/Enums/LanguageCode.php index d647855..5e26183 100644 --- a/app/Enums/LanguageCode.php +++ b/app/Enums/LanguageCode.php @@ -18,7 +18,7 @@ public function getLabel(): ?string }; } - public function getColor(): string | array | null + public function getColor(): string|array|null { return match ($this) { self::DE => 'blue', diff --git a/app/Enums/OfftimeCategory.php b/app/Enums/OfftimeCategory.php index a25e14e..8aaea96 100644 --- a/app/Enums/OfftimeCategory.php +++ b/app/Enums/OfftimeCategory.php @@ -23,7 +23,7 @@ public function getLabel(): ?string }; } - public function getColor(): string | array | null + public function getColor(): string|array|null { return match ($this) { self::Vacation => Color::Lime, diff --git a/app/Enums/PricingUnit.php b/app/Enums/PricingUnit.php index 1fc2c86..eef2fb9 100644 --- a/app/Enums/PricingUnit.php +++ b/app/Enums/PricingUnit.php @@ -20,7 +20,7 @@ public function getLabel(): ?string }; } - public function getColor(): string | array | null + public function getColor(): string|array|null { return match ($this) { self::Hour => 'blue', diff --git a/app/Filament/Pages/Auth/EditProfile.php b/app/Filament/Pages/Auth/EditProfile.php index f7894f6..c1619c4 100644 --- a/app/Filament/Pages/Auth/EditProfile.php +++ b/app/Filament/Pages/Auth/EditProfile.php @@ -19,7 +19,7 @@ public function form(Schema $schema): Schema $this->getPasswordFormComponent(), $this->getPasswordConfirmationFormComponent(), $this->getCurrentPasswordFormComponent(), - ]) + ]), ]); } } diff --git a/app/Filament/Pages/Dashboard.php b/app/Filament/Pages/Dashboard.php index 91be8a4..39f970f 100644 --- a/app/Filament/Pages/Dashboard.php +++ b/app/Filament/Pages/Dashboard.php @@ -10,7 +10,7 @@ class Dashboard extends BaseDashboard { - protected static string | \BackedEnum | null $navigationIcon = 'tabler-chart-pie'; + protected static string|\BackedEnum|null $navigationIcon = 'tabler-chart-pie'; public function getColumns(): int|array { diff --git a/app/Filament/Pages/Taxes.php b/app/Filament/Pages/Taxes.php index 053e437..39aa4fd 100644 --- a/app/Filament/Pages/Taxes.php +++ b/app/Filament/Pages/Taxes.php @@ -10,7 +10,7 @@ class Taxes extends BaseDashboard { protected static string $routePath = 'taxes'; protected static ?string $title = 'Steuern'; - protected static string | \BackedEnum | null $navigationIcon = 'tabler-tax-euro'; + protected static string|\BackedEnum|null $navigationIcon = 'tabler-tax-euro'; public function getColumns(): int|array { diff --git a/app/Filament/Relations/EstimatesRelationManager.php b/app/Filament/Relations/EstimatesRelationManager.php index 22e4baa..aaabeb1 100644 --- a/app/Filament/Relations/EstimatesRelationManager.php +++ b/app/Filament/Relations/EstimatesRelationManager.php @@ -35,7 +35,7 @@ public function table(Table $table): Table ->label(__('title')), TextColumn::make('description') ->label(__('description')) - ->formatStateUsing(fn (string $state): string => nl2br($state)) + ->formatStateUsing(fn(string $state): string => nl2br($state)) ->html(), TextColumn::make('amount') ->label(trans_choice('hour', 2)) diff --git a/app/Filament/Relations/InvoicesRelationManager.php b/app/Filament/Relations/InvoicesRelationManager.php index 0a2e2d1..1d4cd79 100644 --- a/app/Filament/Relations/InvoicesRelationManager.php +++ b/app/Filament/Relations/InvoicesRelationManager.php @@ -35,35 +35,36 @@ public function table(Table $table): Table TextColumn::make('title') ->label(__('title')) ->sortable() - ->description(fn (Invoice $record): string => - ($record->invoiced_at ? __('invoicedAt') . ' ' . Carbon::parse($record->invoiced_at)->isoFormat('LL') : '') . - ($record->paid_at ? ', ' . __('paidAt') . ' ' . Carbon::parse($record->paid_at)->isoFormat('LL') : '') + ->description( + fn(Invoice $record): string + => ($record->invoiced_at ? __('invoicedAt') . ' ' . Carbon::parse($record->invoiced_at)->isoFormat('LL') : '') + . ($record->paid_at ? ', ' . __('paidAt') . ' ' . Carbon::parse($record->paid_at)->isoFormat('LL') : ''), ) - ->tooltip(fn (Invoice $record): ?string => $record->description), + ->tooltip(fn(Invoice $record): ?string => $record->description), TextColumn::make('price') ->label(__('price')) ->money('eur') ->fontFamily(FontFamily::Mono) - ->description(fn (Invoice $record): string => $record->pricing_unit->getLabel()), + ->description(fn(Invoice $record): string => $record->pricing_unit->getLabel()), TextColumn::make('net') ->label(__('net')) ->money('eur') ->fontFamily(FontFamily::Mono) - ->state(fn (Invoice $record): float => $record->net) - ->description(fn (Invoice $record): string => $record->hours . ' ' . trans_choice('hour', $record->hours)), + ->state(fn(Invoice $record): float => $record->net) + ->description(fn(Invoice $record): string => $record->hours . ' ' . trans_choice('hour', $record->hours)), TextColumn::make('total') ->label(__('total')) ->money('eur') ->fontFamily(FontFamily::Mono) - ->state(fn (Invoice $record): float => $record->final) - ->description(fn (Invoice $record): string => Number::currency($record->vat, 'eur') . ' ' . __('vat')), + ->state(fn(Invoice $record): float => $record->final) + ->description(fn(Invoice $record): string => Number::currency($record->vat, 'eur') . ' ' . __('vat')), ]) ->headerActions([ CreateAction::make() ->icon('tabler-plus') ->label(__('create')) ->schema(InvoiceResource::formFields(6, false)) - ->visible(fn (): bool => $this->getOwnerRecord() instanceof Project) + ->visible(fn(): bool => $this->getOwnerRecord() instanceof Project) ->mountUsing(function (Schema $schema) { $schema->fill(); if ($this->getOwnerRecord() instanceof Project) { @@ -86,7 +87,7 @@ public function table(Table $table): Table ->slideOver() ->modalWidth(Width::ExtraLarge), ]) - ->icon('tabler-dots-vertical') + ->icon('tabler-dots-vertical'), ]) ->toolbarActions([ BulkActionGroup::make([ @@ -99,7 +100,7 @@ public function table(Table $table): Table ->icon('tabler-plus') ->label(__('create')) ->schema(InvoiceResource::formFields(6, false)) - ->visible(fn (): bool => $this->getOwnerRecord() instanceof Project) + ->visible(fn(): bool => $this->getOwnerRecord() instanceof Project) ->mountUsing(function (Schema $schema) { $schema->fill(); if ($this->getOwnerRecord() instanceof Project) { diff --git a/app/Filament/Relations/PositionsRelationManager.php b/app/Filament/Relations/PositionsRelationManager.php index a97ae0e..29bf29c 100644 --- a/app/Filament/Relations/PositionsRelationManager.php +++ b/app/Filament/Relations/PositionsRelationManager.php @@ -33,14 +33,14 @@ public function table(Table $table): Table ->columns([ TextColumn::make('description') ->label(__('description')) - ->formatStateUsing(fn (string $state): string => nl2br($state)) + ->formatStateUsing(fn(string $state): string => nl2br($state)) ->html(), TextColumn::make('amount') ->label(trans_choice('hour', 2)) - ->state(fn (Position $record): float => $record->duration) + ->state(fn(Position $record): float => $record->duration) ->weight(FontWeight::ExtraBold) ->fontFamily(FontFamily::Mono) - ->description(fn (Position $record): string => $record->time_range), + ->description(fn(Position $record): string => $record->time_range), ToggleColumn::make('remote') ->label(__('remote')), ]) diff --git a/app/Filament/Relations/ProjectsRelationManager.php b/app/Filament/Relations/ProjectsRelationManager.php index 2cfdcce..c64e870 100644 --- a/app/Filament/Relations/ProjectsRelationManager.php +++ b/app/Filament/Relations/ProjectsRelationManager.php @@ -30,21 +30,22 @@ public function table(Table $table): Table ->label(__('title')) ->searchable() ->sortable() - ->tooltip(fn (Project $record): ?string => $record->description), + ->tooltip(fn(Project $record): ?string => $record->description), TextColumn::make('date_range') ->label(__('dateRange')) - ->state(fn (Project $record): string => Carbon::parse($record->start_at) - ->isoFormat('ll') . ' - ' . ($record->due_at ? Carbon::parse($record->due_at)->isoFormat('ll') : '∞') + ->state( + fn(Project $record): string => Carbon::parse($record->start_at) + ->isoFormat('ll') . ' - ' . ($record->due_at ? Carbon::parse($record->due_at)->isoFormat('ll') : '∞'), ), TextColumn::make('scope') ->label(__('scope')) - ->state(fn (Project $record): string => $record->scope_range), + ->state(fn(Project $record): string => $record->scope_range), TextColumn::make('price_per_unit') ->label(__('price')) - ->state(fn (Project $record): string => $record->price_per_unit), + ->state(fn(Project $record): string => $record->price_per_unit), TextColumn::make('progress') ->label(__('progress')) - ->state(fn (Project $record): string => $record->hours_with_label), + ->state(fn(Project $record): string => $record->hours_with_label), TextColumn::make('created_at') ->label(__('createdAt')) ->datetime('j. F Y, H:i:s') @@ -84,7 +85,7 @@ public function table(Table $table): Table ->slideOver() ->modalWidth(Width::Large), ]) - ->icon('tabler-dots-vertical') + ->icon('tabler-dots-vertical'), ]); } diff --git a/app/Filament/Resources/ClientResource.php b/app/Filament/Resources/ClientResource.php index 144c7d3..ed3d5ec 100644 --- a/app/Filament/Resources/ClientResource.php +++ b/app/Filament/Resources/ClientResource.php @@ -37,7 +37,7 @@ class ClientResource extends Resource { protected static ?string $model = Client::class; - protected static string | \BackedEnum | null $navigationIcon = 'tabler-users'; + protected static string|\BackedEnum|null $navigationIcon = 'tabler-users'; protected static ?int $navigationSort = 10; public static function getNavigationBadge(): ?string @@ -70,7 +70,7 @@ public static function table(Table $table): Table ->label(__('name')) ->searchable() ->sortable() - ->description(fn (Client $record): string => $record->full_address) + ->description(fn(Client $record): string => $record->full_address) ->wrap(), TextColumn::make('language') ->label(__('language')) @@ -80,14 +80,14 @@ public static function table(Table $table): Table ->label(__('net')) ->money('eur') ->fontFamily(FontFamily::Mono) - ->state(fn (Client $record): float => $record->net) - ->description(fn (Client $record): string => $record->hours . ' ' . trans_choice('hour', $record->hours)), + ->state(fn(Client $record): float => $record->net) + ->description(fn(Client $record): string => $record->hours . ' ' . trans_choice('hour', $record->hours)), TextColumn::make('days_to_pay') ->label(__('payment')) ->abbr(__('averagePaymentDuration'), asTooltip: true) ->numeric(1) ->fontFamily(FontFamily::Mono) - ->state(fn (Client $record): float => $record->avg_payment_delay) + ->state(fn(Client $record): float => $record->avg_payment_delay) ->description(trans_choice('day', 2)), TextColumn::make('created_at') ->label(__('createdAt')) @@ -107,9 +107,9 @@ public static function table(Table $table): Table ->recordActions(ActionGroup::make([ EditAction::make()->icon('tabler-edit'), Action::make('kontaktieren') - ->disabled(fn (Client $record) => !boolval($record->email)) + ->disabled(fn(Client $record) => !boolval($record->email)) ->icon('tabler-mail') - ->schema(fn (Client $record) => [ + ->schema(fn(Client $record) => [ TextInput::make('subject') ->label(__('subject')) ->required(), @@ -118,12 +118,12 @@ public static function table(Table $table): Table ->required() ->default(__("email.template.contact.body", [ 'name' => $record->name, - 'sender' => Setting::get('name') + 'sender' => Setting::get('name'), ])), ]) ->action(function (Client $record, array $data) { Mail::to($record->email)->send( - (new ContactClient(body: $data['content']))->subject($data['subject']) + (new ContactClient(body: $data['content']))->subject($data['subject']), ); }) ->slideOver() diff --git a/app/Filament/Resources/EstimateResource.php b/app/Filament/Resources/EstimateResource.php index 30d2ceb..b82a9ce 100644 --- a/app/Filament/Resources/EstimateResource.php +++ b/app/Filament/Resources/EstimateResource.php @@ -28,7 +28,7 @@ class EstimateResource extends Resource { protected static ?string $model = Estimate::class; - protected static string | \BackedEnum | null $navigationIcon = 'tabler-clock-code'; + protected static string|\BackedEnum|null $navigationIcon = 'tabler-clock-code'; protected static ?int $navigationSort = 25; public static function form(Schema $schema): Schema @@ -43,12 +43,12 @@ public static function table(Table $table): Table ->columns([ ColorColumn::make('project.client.color') ->label('') - ->tooltip(fn (Estimate $record): ?string => $record->project?->client?->name), + ->tooltip(fn(Estimate $record): ?string => $record->project?->client?->name), TextColumn::make('title') ->label(__('title')) ->searchable() ->sortable() - ->description(fn (Estimate $record): string => substr($record->description, 0, 75) . '...'), + ->description(fn(Estimate $record): string => substr($record->description, 0, 75) . '...'), TextColumn::make('amount') ->label(trans_choice('hour', 2)) ->numeric() @@ -73,7 +73,7 @@ public static function table(Table $table): Table ->filters([ SelectFilter::make('project') ->label(trans_choice('project', 1)) - ->relationship('project', 'title') + ->relationship('project', 'title'), ]) ->recordActions( ActionGroup::make([ @@ -89,7 +89,7 @@ public static function table(Table $table): Table ->modalWidth(Width::Large), DeleteAction::make()->icon('tabler-trash')->requiresConfirmation(), ]) - ->icon('tabler-dots-vertical') + ->icon('tabler-dots-vertical'), ) ->toolbarActions([ BulkActionGroup::make([ diff --git a/app/Filament/Resources/ExpenseResource.php b/app/Filament/Resources/ExpenseResource.php index 10e9fd7..66e58d2 100644 --- a/app/Filament/Resources/ExpenseResource.php +++ b/app/Filament/Resources/ExpenseResource.php @@ -33,7 +33,7 @@ class ExpenseResource extends Resource { protected static ?string $model = Expense::class; - protected static string | \BackedEnum | null $navigationIcon = 'tabler-credit-card'; + protected static string|\BackedEnum|null $navigationIcon = 'tabler-credit-card'; protected static ?int $navigationSort = 40; public static function form(Schema $schema): Schema @@ -65,8 +65,8 @@ public static function table(Table $table): Table ->label(__('vat')) ->money('eur') ->fontFamily(FontFamily::Mono) - ->state(fn (Expense $record): float => $record->vat) - ->color(fn (string $state): string => $state == 0 ? 'gray' : 'normal') + ->state(fn(Expense $record): float => $record->vat) + ->color(fn(string $state): string => $state == 0 ? 'gray' : 'normal') ->sortable(), TextColumn::make('quantity') ->label(__('quantity')) @@ -94,7 +94,7 @@ public static function table(Table $table): Table ->filters([ SelectFilter::make('category') ->label(__('category')) - ->options(ExpenseCategory::options()) + ->options(ExpenseCategory::options()), ]) ->recordActions( ActionGroup::make([ @@ -110,7 +110,7 @@ public static function table(Table $table): Table ->modalWidth(Width::Large), DeleteAction::make()->icon('tabler-trash')->requiresConfirmation(), ]) - ->icon('tabler-dots-vertical') + ->icon('tabler-dots-vertical'), ) ->toolbarActions([ BulkActionGroup::make([ @@ -216,7 +216,7 @@ public static function formFields(int $columns = 12, bool $useSection = true): a ->suffixIcon('tabler-receipt-tax') ->columnSpan($columns / 2) ->required() - ->hidden(fn (Get $get): bool => !$get('taxable')), + ->hidden(fn(Get $get): bool => !$get('taxable')), Textarea::make('description') ->label(__('description')) ->maxLength(65535) diff --git a/app/Filament/Resources/ExpenseResource/Pages/ListExpenses.php b/app/Filament/Resources/ExpenseResource/Pages/ListExpenses.php index 886b074..5d753b2 100644 --- a/app/Filament/Resources/ExpenseResource/Pages/ListExpenses.php +++ b/app/Filament/Resources/ExpenseResource/Pages/ListExpenses.php @@ -14,28 +14,28 @@ class ListExpenses extends ListRecords { protected static string $resource = ExpenseResource::class; - protected function getHeaderActions(): array - { - return [ - CreateAction::make() - ->icon('tabler-plus') - ->schema(ExpenseResource::formFields(6, false)) - ->slideOver() - ->modalWidth(Width::Large), - ]; - } - public function getTabs(): array { return [ 'deliverables' => Tab::make() ->label(__('deliverables')) - ->modifyQueryUsing(fn (Builder $query) => $query->whereIn('category', ExpenseCategory::deliverableCategories())), + ->modifyQueryUsing(fn(Builder $query) => $query->whereIn('category', ExpenseCategory::deliverableCategories())), 'tax' => Tab::make() ->label(__('taxes')) - ->modifyQueryUsing(fn (Builder $query) => $query->whereIn('category', ExpenseCategory::taxCategories())), + ->modifyQueryUsing(fn(Builder $query) => $query->whereIn('category', ExpenseCategory::taxCategories())), 'all' => Tab::make() ->label(__('all')), ]; } + + protected function getHeaderActions(): array + { + return [ + CreateAction::make() + ->icon('tabler-plus') + ->schema(ExpenseResource::formFields(6, false)) + ->slideOver() + ->modalWidth(Width::Large), + ]; + } } diff --git a/app/Filament/Resources/GiftResource.php b/app/Filament/Resources/GiftResource.php index 052e4ab..122c62d 100644 --- a/app/Filament/Resources/GiftResource.php +++ b/app/Filament/Resources/GiftResource.php @@ -27,7 +27,7 @@ class GiftResource extends Resource { protected static ?string $model = Gift::class; - protected static string | \BackedEnum | null $navigationIcon = 'tabler-gift'; + protected static string|\BackedEnum|null $navigationIcon = 'tabler-gift'; protected static ?int $navigationSort = 50; public static function form(Schema $schema): Schema @@ -51,7 +51,7 @@ public static function table(Table $table): Table ->label(__('name')) ->searchable() ->sortable() - ->description(fn (Gift $record): string => $record->email ?? ''), + ->description(fn(Gift $record): string => $record->email ?? ''), TextColumn::make('amount') ->label(__('amount')) ->money('eur') @@ -87,7 +87,7 @@ public static function table(Table $table): Table ->modalWidth(Width::Large), DeleteAction::make()->icon('tabler-trash')->requiresConfirmation(), ]) - ->icon('tabler-dots-vertical') + ->icon('tabler-dots-vertical'), ) ->toolbarActions([ BulkActionGroup::make([ diff --git a/app/Filament/Resources/InvoiceResource.php b/app/Filament/Resources/InvoiceResource.php index 2498ab3..70e5bf8 100644 --- a/app/Filament/Resources/InvoiceResource.php +++ b/app/Filament/Resources/InvoiceResource.php @@ -24,7 +24,6 @@ use Filament\Forms\Components\Textarea; use Filament\Forms\Components\TextInput; use Filament\Forms\Components\Toggle; -use Filament\Infolists\Components\TextEntry; use Filament\Notifications\Notification; use Filament\Resources\Resource; use Filament\Schemas\Components\Grid; @@ -45,7 +44,7 @@ class InvoiceResource extends Resource { protected static ?string $model = Invoice::class; - protected static string | \BackedEnum | null $navigationIcon = 'tabler-file-stack'; + protected static string|\BackedEnum|null $navigationIcon = 'tabler-file-stack'; protected static ?int $navigationSort = 30; public static function getNavigationBadge(): ?string @@ -70,13 +69,13 @@ public static function form(Schema $schema): Schema ->columns(10) ->components([ Section::make() - ->columnSpan(['lg' => fn (?Invoice $record) => !$record?->project ? 10 : 8]) + ->columnSpan(['lg' => fn(?Invoice $record) => !$record?->project ? 10 : 8]) ->schema(self::formFields(12, false)), Grid::make() - ->hidden(fn (?Invoice $record) => !$record?->project) + ->hidden(fn(?Invoice $record) => !$record?->project) ->columns(1) ->columnSpan(['lg' => 2]) - ->schema(fn (?Invoice $record) => [ + ->schema(fn(?Invoice $record) => [ Stat::make('project', '...') ->label(trans_choice('project', 1)) ->value($record->project?->progress_percent ?? '-') @@ -85,7 +84,7 @@ public static function form(Schema $schema): Schema ->label(trans_choice('invoice', 1)) ->value($record->hours_formatted) ->description(new HtmlString( - "$record->positions_formatted
$record->net_formatted " . __('net') + "$record->positions_formatted
$record->net_formatted " . __('net'), )), ]), ]); @@ -101,31 +100,32 @@ public static function table(Table $table): Table ->label(__('title')) ->searchable() ->sortable() - ->description(fn (Invoice $record): string => $record->project?->client?->name) - ->tooltip(fn (Invoice $record): ?string => $record->description), + ->description(fn(Invoice $record): string => $record->project?->client?->name) + ->tooltip(fn(Invoice $record): ?string => $record->description), TextColumn::make('price') ->label(__('price')) ->money('eur') ->fontFamily(FontFamily::Mono) - ->description(fn (Invoice $record): string => $record->pricing_unit->getLabel()), + ->description(fn(Invoice $record): string => $record->pricing_unit->getLabel()), TextColumn::make('net') ->label(__('net')) ->money('eur') ->fontFamily(FontFamily::Mono) - ->state(fn (Invoice $record): float => $record->net) - ->description(fn (Invoice $record): string => $record->hours . ' ' . trans_choice('hour', $record->hours)), + ->state(fn(Invoice $record): float => $record->net) + ->description(fn(Invoice $record): string => $record->hours . ' ' . trans_choice('hour', $record->hours)), TextColumn::make('total') ->label(__('total')) ->money('eur') ->fontFamily(FontFamily::Mono) - ->state(fn (Invoice $record): float => $record->final) - ->description(fn (Invoice $record): string => Number::currency($record->vat, 'eur') . ' ' . __('vat')), + ->state(fn(Invoice $record): float => $record->final) + ->description(fn(Invoice $record): string => Number::currency($record->vat, 'eur') . ' ' . __('vat')), TextColumn::make('invoiced_at') ->label(__('invoiceDates')) ->date('j. F Y') - ->description(fn (Invoice $record): string => $record->paid_at + ->description( + fn(Invoice $record): string => $record->paid_at ? Carbon::parse($record->paid_at)->isoFormat('LL') - : '' + : '', ), TextColumn::make('created_at') ->label(__('createdAt')) @@ -178,7 +178,7 @@ public static function table(Table $table): Table ->label(__('send')) ->icon('tabler-mail-forward') ->hidden(fn(Invoice $record) => $record->status != InvoiceStatus::RUNNING) - ->url(fn (Invoice $record): string => 'mailto:' . $record->project?->client?->email + ->url(fn(Invoice $record): string => 'mailto:' . $record->project?->client?->email . '?subject=' . rawurlencode(trans_choice('invoice', 1, [], $record->project?->client?->language->value)) . ' ' . $record->current_number . '&body=' . rawurlencode(__('email.template.invoicing.body.url', ['title' => $record->project?->title], $record->project?->client?->language->value))), Action::make('issue') @@ -213,12 +213,12 @@ public static function table(Table $table): Table ->label(__('paymentReminder')) ->icon('tabler-mail-exclamation') ->hidden(fn(Invoice $record) => $record->status != InvoiceStatus::SENT) - ->url(fn (Invoice $record): string => 'mailto:' . $record->project?->client?->email + ->url(fn(Invoice $record): string => 'mailto:' . $record->project?->client?->email . '?subject=' . rawurlencode(__('paymentReminder') . ' ' . trans_choice('invoice', 1, [], $record->project?->client?->language->value)) . ' ' . $record->final_number . '&body=' . rawurlencode(__('email.template.paymentReminder.body.url', ['number' => $record->final_number], $record->project?->client?->language->value))), DeleteAction::make()->icon('tabler-trash')->requiresConfirmation(), ]) - ->icon('tabler-dots-vertical') + ->icon('tabler-dots-vertical'), ) ->toolbarActions([ BulkActionGroup::make([ @@ -279,7 +279,7 @@ public static function formFields(int $columns = 12, bool $useSection = true): a Select::make('project_id') ->label(trans_choice('project', 1)) ->relationship('project', 'title') - ->getOptionLabelFromRecordUsing(fn (Project $record) => "{$record->title} ({$record->client->name})") + ->getOptionLabelFromRecordUsing(fn(Project $record) => "{$record->title} ({$record->client->name})") ->searchable() ->preload() ->suffixIcon('tabler-package') @@ -289,12 +289,12 @@ public static function formFields(int $columns = 12, bool $useSection = true): a ->label(__('transitory')) ->inline(false) ->hintIcon('tabler-info-circle', __('invoice.onlyTransitory')) - ->columnSpan($half/2), + ->columnSpan($half / 2), Toggle::make('undated') ->label(__('undated')) ->inline(false) ->hintIcon('tabler-info-circle', __('hidePositionsDate')) - ->columnSpan($half/2), + ->columnSpan($half / 2), TextInput::make('title') ->label(__('title')) ->required() @@ -311,13 +311,13 @@ public static function formFields(int $columns = 12, bool $useSection = true): a ->minValue(0.01) ->suffixIcon('tabler-currency-euro') ->required() - ->columnSpan($half/2), + ->columnSpan($half / 2), Select::make('pricing_unit') ->label(__('pricingUnit')) ->options(PricingUnit::class) ->suffixIcon('tabler-clock-2') ->required() - ->columnSpan($half/2), + ->columnSpan($half / 2), TextInput::make('discount') ->label(__('discount')) ->numeric() @@ -325,7 +325,7 @@ public static function formFields(int $columns = 12, bool $useSection = true): a ->minValue(0.01) ->suffixIcon('tabler-currency-euro') ->helperText(__('priceBeforeTax')) - ->columnSpan($half/2), + ->columnSpan($half / 2), TextInput::make('deduction') ->label(__('deduction')) ->numeric() @@ -333,13 +333,13 @@ public static function formFields(int $columns = 12, bool $useSection = true): a ->minValue(0.01) ->suffixIcon('tabler-currency-euro') ->helperText(__('priceAfterTax')) - ->columnSpan($half/2), + ->columnSpan($half / 2), Toggle::make('taxable') ->label(__('taxable')) ->inline(false) ->default(true) ->live() - ->columnSpan($half/2), + ->columnSpan($half / 2), TextInput::make('vat_rate') ->label(__('vatRate')) ->numeric() @@ -347,18 +347,18 @@ public static function formFields(int $columns = 12, bool $useSection = true): a ->minValue(0.01) ->default(0.19) ->suffixIcon('tabler-receipt-tax') - ->hidden(fn (Get $get): bool => ! $get('taxable')) - ->columnSpan($half/2), + ->hidden(fn(Get $get): bool => ! $get('taxable')) + ->columnSpan($half / 2), DatePicker::make('invoiced_at') ->label(__('invoicedAt')) ->weekStartsOnMonday() ->suffixIcon('tabler-calendar-up') - ->columnSpan($half/2), + ->columnSpan($half / 2), DatePicker::make('paid_at') ->label(__('paidAt')) ->weekStartsOnMonday() ->suffixIcon('tabler-calendar-down') - ->columnSpan($half/2), + ->columnSpan($half / 2), ]; return $useSection diff --git a/app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php b/app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php index add5d45..da224e0 100644 --- a/app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php +++ b/app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php @@ -17,6 +17,11 @@ class EditInvoice extends EditRecord { protected static string $resource = InvoiceResource::class; + public function getFooterWidgetsColumns(): int|array + { + return 12; + } + protected function getHeaderActions(): array { return [ @@ -40,11 +45,6 @@ protected function getHeaderActions(): array ]; } - public function getFooterWidgetsColumns(): int|array - { - return 12; - } - protected function getFooterWidgets(): array { return [ diff --git a/app/Filament/Resources/InvoiceResource/Pages/ListInvoices.php b/app/Filament/Resources/InvoiceResource/Pages/ListInvoices.php index 206db14..ae156ef 100644 --- a/app/Filament/Resources/InvoiceResource/Pages/ListInvoices.php +++ b/app/Filament/Resources/InvoiceResource/Pages/ListInvoices.php @@ -15,17 +15,6 @@ class ListInvoices extends ListRecords { protected static string $resource = InvoiceResource::class; - protected function getHeaderActions(): array - { - return [ - CreateAction::make() - ->icon('tabler-plus') - ->schema(InvoiceResource::formFields(6, false)) - ->slideOver() - ->modalWidth(Width::ExtraLarge), - ]; - } - public function getTabs(): array { $active = Invoice::active(); @@ -40,19 +29,30 @@ public function getTabs(): array 'active' => Tab::make() ->label(__('inProgress', ['net' => Number::currency($activeNet, 'eur') ])) ->badge($activeCount) - ->modifyQueryUsing(fn (Builder $query) => $query->active()->orderBy('updated_at', 'desc')), + ->modifyQueryUsing(fn(Builder $query) => $query->active()->orderBy('updated_at', 'desc')), 'waiting' => Tab::make() ->label(__('waitingForPayment', ['net' => Number::currency($waitingNet, 'eur') ])) ->badge($waitingCount) ->badgeColor($waitingCount > 0 ? 'warning' : 'gray') - ->modifyQueryUsing(fn (Builder $query) => $query->waiting()->orderBy('invoiced_at', 'desc')), + ->modifyQueryUsing(fn(Builder $query) => $query->waiting()->orderBy('invoiced_at', 'desc')), 'finished' => Tab::make() ->label(__('finished')) ->badge($finishedCount) ->badgeColor('gray') - ->modifyQueryUsing(fn (Builder $query) => $query->finished()->orderBy('paid_at', 'desc')), + ->modifyQueryUsing(fn(Builder $query) => $query->finished()->orderBy('paid_at', 'desc')), 'all' => Tab::make() ->label(__('all')), ]; } + + protected function getHeaderActions(): array + { + return [ + CreateAction::make() + ->icon('tabler-plus') + ->schema(InvoiceResource::formFields(6, false)) + ->slideOver() + ->modalWidth(Width::ExtraLarge), + ]; + } } diff --git a/app/Filament/Resources/InvoiceResource/Widgets/ActiveInvoices.php b/app/Filament/Resources/InvoiceResource/Widgets/ActiveInvoices.php index 598e2a9..50e15d8 100644 --- a/app/Filament/Resources/InvoiceResource/Widgets/ActiveInvoices.php +++ b/app/Filament/Resources/InvoiceResource/Widgets/ActiveInvoices.php @@ -14,7 +14,7 @@ class ActiveInvoices extends TableWidget { public ?Invoice $record = null; - protected int | string | array $columnSpan = 6; + protected int|string|array $columnSpan = 6; public function table(Table $table): Table { @@ -33,7 +33,7 @@ public function table(Table $table): Table Action::make('edit') ->label('') ->icon('tabler-edit') - ->url(fn (Invoice $i): string => InvoiceResource::getUrl('edit', ['record' => $i])), + ->url(fn(Invoice $i): string => InvoiceResource::getUrl('edit', ['record' => $i])), ]); } } diff --git a/app/Filament/Resources/InvoiceResource/Widgets/ClientInvoices.php b/app/Filament/Resources/InvoiceResource/Widgets/ClientInvoices.php index d4fe0bd..57ff653 100644 --- a/app/Filament/Resources/InvoiceResource/Widgets/ClientInvoices.php +++ b/app/Filament/Resources/InvoiceResource/Widgets/ClientInvoices.php @@ -14,15 +14,15 @@ class ClientInvoices extends TableWidget { public ?Invoice $record = null; - protected int | string | array $columnSpan = 6; + protected int|string|array $columnSpan = 6; public function table(Table $table): Table { return $table ->heading(__('otherInvoices')) ->query( - Invoice::whereHas('project', fn ($query) => $query->where('client_id', $this->record?->project->client_id)) - ->whereNot('id', $this->record?->id) + Invoice::whereHas('project', fn($query) => $query->where('client_id', $this->record?->project->client_id)) + ->whereNot('id', $this->record?->id), ) ->paginated([8]) ->defaultSort('created_at', 'desc') @@ -39,7 +39,7 @@ public function table(Table $table): Table Action::make('edit') ->label('') ->icon('tabler-edit') - ->url(fn (Invoice $i): string => InvoiceResource::getUrl('edit', ['record' => $i])), + ->url(fn(Invoice $i): string => InvoiceResource::getUrl('edit', ['record' => $i])), ]); } } diff --git a/app/Filament/Resources/OfftimeResource.php b/app/Filament/Resources/OfftimeResource.php index f05f7e9..b8bcd92 100644 --- a/app/Filament/Resources/OfftimeResource.php +++ b/app/Filament/Resources/OfftimeResource.php @@ -28,7 +28,7 @@ class OfftimeResource extends Resource { protected static ?string $model = Offtime::class; - protected static string | \BackedEnum | null $navigationIcon = 'tabler-beach'; + protected static string|\BackedEnum|null $navigationIcon = 'tabler-beach'; protected static ?int $navigationSort = 60; public static function form(Schema $schema): Schema @@ -46,7 +46,7 @@ public static function table(Table $table): Table ->sortable(), TextColumn::make('days') ->label(trans_choice('day', 2)) - ->state(fn (Offtime $record): string => $record->days_count ?? '') + ->state(fn(Offtime $record): string => $record->days_count ?? '') ->fontFamily(FontFamily::Mono) ->sortable(), TextColumn::make('category') @@ -60,7 +60,7 @@ public static function table(Table $table): Table ]) ->filters([ SelectFilter::make('category') - ->options(OfftimeCategory::options()) + ->options(OfftimeCategory::options()), ]) ->recordActions( ActionGroup::make([ @@ -76,7 +76,7 @@ public static function table(Table $table): Table ->modalWidth(Width::Large), DeleteAction::make()->icon('tabler-trash')->requiresConfirmation(), ]) - ->icon('tabler-dots-vertical') + ->icon('tabler-dots-vertical'), ) ->toolbarActions([ BulkActionGroup::make([ diff --git a/app/Filament/Resources/PositionResource.php b/app/Filament/Resources/PositionResource.php index 866c1cf..ef3188f 100644 --- a/app/Filament/Resources/PositionResource.php +++ b/app/Filament/Resources/PositionResource.php @@ -39,7 +39,7 @@ class PositionResource extends Resource { protected static ?string $model = Position::class; - protected static string | \BackedEnum | null $navigationIcon = 'tabler-list-details'; + protected static string|\BackedEnum|null $navigationIcon = 'tabler-list-details'; protected static ?int $navigationSort = 35; public static function form(Schema $schema): Schema @@ -53,19 +53,19 @@ public static function table(Table $table): Table ->columns([ ColorColumn::make('invoice.project.client.color') ->label('') - ->tooltip(fn (Position $record): ?string => $record->invoice?->project?->client?->name), + ->tooltip(fn(Position $record): ?string => $record->invoice?->project?->client?->name), TextColumn::make('description') ->label(__('description')) ->searchable() - ->tooltip(fn (Position $record): ?string => $record->invoice?->title) - ->formatStateUsing(fn (string $state): string => nl2br($state)) + ->tooltip(fn(Position $record): ?string => $record->invoice?->title) + ->formatStateUsing(fn(string $state): string => nl2br($state)) ->html(), TextColumn::make('amount') ->label(trans_choice('hour', 2)) - ->state(fn (Position $record): float => $record->duration) + ->state(fn(Position $record): float => $record->duration) ->fontFamily(FontFamily::Mono) ->weight(FontWeight::ExtraBold) - ->description(fn (Position $record): string => $record->time_range), + ->description(fn(Position $record): string => $record->time_range), ToggleColumn::make('remote') ->label(__('remote')), TextColumn::make('created_at') @@ -92,7 +92,7 @@ public static function table(Table $table): Table ->preload(), SelectFilter::make('invoice') ->label(trans_choice('invoice', 1)) - ->relationship('invoice', 'title', fn (Builder $query) => $query->active()->orderByDesc('created_at')) + ->relationship('invoice', 'title', fn(Builder $query) => $query->active()->orderByDesc('created_at')) ->searchable() ->preload(), Filter::make('created_at') @@ -107,11 +107,11 @@ public static function table(Table $table): Table return $query ->when( $data['earliest'], - fn (Builder $query, $date): Builder => $query->whereDate('started_at', '>=', $date), + fn(Builder $query, $date): Builder => $query->whereDate('started_at', '>=', $date), ) ->when( $data['latest'], - fn (Builder $query, $date): Builder => $query->whereDate('finished_at', '<=', $date), + fn(Builder $query, $date): Builder => $query->whereDate('finished_at', '<=', $date), ); }), TernaryFilter::make('remote'), @@ -131,7 +131,7 @@ public static function table(Table $table): Table ->modalWidth(Width::TwoExtraLarge), DeleteAction::make()->icon('tabler-trash')->requiresConfirmation(), ]) - ->icon('tabler-dots-vertical') + ->icon('tabler-dots-vertical'), ) ->toolbarActions([ BulkActionGroup::make([ @@ -206,7 +206,7 @@ public static function formFields(int $columns = 12, bool $useSection = true): a if ($started >= $finished || !$started->isSameDay($finished)) { $set( 'finished_at', - $started->addMinutes($previous->diffInMinutes($finished))->toDateTimeString() + $started->addMinutes($previous->diffInMinutes($finished))->toDateTimeString(), ); } }) diff --git a/app/Filament/Resources/PositionResource/Widgets/RecentPositionsChart.php b/app/Filament/Resources/PositionResource/Widgets/RecentPositionsChart.php index 6fa6c85..f8af493 100644 --- a/app/Filament/Resources/PositionResource/Widgets/RecentPositionsChart.php +++ b/app/Filament/Resources/PositionResource/Widgets/RecentPositionsChart.php @@ -11,13 +11,13 @@ class RecentPositionsChart extends ChartWidget { - protected ?string $maxHeight = '180px'; public ?string $filter = '60'; - public int | string | array $columnSpan = [ + public int|string|array $columnSpan = [ 'sm' => 12, 'xl' => 6, ]; + protected ?string $maxHeight = '180px'; public function getHeading(): string { @@ -33,7 +33,7 @@ protected function getData(): array { $labels = []; $datasets = []; - $period = CarbonPeriod::create(Carbon::now()->subDays((int)$this->filter), '1 day', 'now'); + $period = CarbonPeriod::create(Carbon::now()->subDays((int) $this->filter), '1 day', 'now'); foreach ($period as $i => $date) { // X-Axis labels $labels[] = $date->isoFormat('dd, D. MMM'); diff --git a/app/Filament/Resources/ProjectResource.php b/app/Filament/Resources/ProjectResource.php index 266cdef..f1c6a77 100644 --- a/app/Filament/Resources/ProjectResource.php +++ b/app/Filament/Resources/ProjectResource.php @@ -37,7 +37,7 @@ class ProjectResource extends Resource { protected static ?string $model = Project::class; - protected static string | \BackedEnum | null $navigationIcon = 'tabler-package'; + protected static string|\BackedEnum|null $navigationIcon = 'tabler-package'; protected static ?int $navigationSort = 20; public static function getNavigationBadge(): ?string @@ -65,24 +65,26 @@ public static function table(Table $table): Table ->label(__('title')) ->searchable() ->sortable() - ->description(fn (Project $record): string => $record->client?->name) - ->tooltip(fn (Project $record): ?string => $record->description), + ->description(fn(Project $record): string => $record->client?->name) + ->tooltip(fn(Project $record): ?string => $record->description), TextColumn::make('date_range') ->label(__('dateRange')) - ->state(fn (Project $record): string => Carbon::parse($record->start_at) - ->longAbsoluteDiffForHumans(Carbon::parse($record->due_at), 2) + ->state( + fn(Project $record): string => Carbon::parse($record->start_at) + ->longAbsoluteDiffForHumans(Carbon::parse($record->due_at), 2), ) - ->description(fn (Project $record): string => Carbon::parse($record->start_at) - ->isoFormat('ll') . ' - ' . ($record->due_at ? Carbon::parse($record->due_at)->isoFormat('ll') : '∞') + ->description( + fn(Project $record): string => Carbon::parse($record->start_at) + ->isoFormat('ll') . ' - ' . ($record->due_at ? Carbon::parse($record->due_at)->isoFormat('ll') : '∞'), ), TextColumn::make('scope') ->label(__('scope')) - ->state(fn (Project $record): string => $record->scope_range) - ->description(fn (Project $record): string => $record->price_per_unit), + ->state(fn(Project $record): string => $record->scope_range) + ->description(fn(Project $record): string => $record->price_per_unit), TextColumn::make('progress') ->label(__('progress')) - ->state(fn (Project $record): string => $record->hours_with_label) - ->description(fn (Project $record): string => $record->progress_percent), + ->state(fn(Project $record): string => $record->hours_with_label) + ->description(fn(Project $record): string => $record->progress_percent), TextColumn::make('created_at') ->label(__('createdAt')) ->datetime('j. F Y, H:i:s') @@ -122,7 +124,7 @@ public static function table(Table $table): Table }), DeleteAction::make()->icon('tabler-trash')->requiresConfirmation(), ]) - ->icon('tabler-dots-vertical') + ->icon('tabler-dots-vertical'), ) ->toolbarActions([ BulkActionGroup::make([ diff --git a/app/Filament/Resources/ProjectResource/Pages/ListProjects.php b/app/Filament/Resources/ProjectResource/Pages/ListProjects.php index 54be1bc..98d4e10 100644 --- a/app/Filament/Resources/ProjectResource/Pages/ListProjects.php +++ b/app/Filament/Resources/ProjectResource/Pages/ListProjects.php @@ -14,40 +14,40 @@ class ListProjects extends ListRecords { protected static string $resource = ProjectResource::class; - protected function getHeaderActions(): array - { - return [ - CreateAction::make() - ->icon('tabler-plus') - ->schema(ProjectResource::formFields(6, false)) - ->slideOver() - ->modalWidth(Width::Large), - ]; - } - public function getTabs(): array { return [ 'active' => Tab::make() ->label(__('active')) ->badge(Project::active()->count()) - ->modifyQueryUsing(fn (Builder $query) => $query->active()), + ->modifyQueryUsing(fn(Builder $query) => $query->active()), 'upcoming' => Tab::make() ->label(__('upcoming')) ->badge(Project::upcoming()->count()) - ->modifyQueryUsing(fn (Builder $query) => $query->upcoming()), + ->modifyQueryUsing(fn(Builder $query) => $query->upcoming()), 'finished' => Tab::make() ->label(__('finished')) ->badge(Project::finished()->count()) ->badgeColor('gray') - ->modifyQueryUsing(fn (Builder $query) => $query->finished()), + ->modifyQueryUsing(fn(Builder $query) => $query->finished()), 'aborted' => Tab::make() ->label(__('aborted')) ->badge(Project::aborted()->count()) ->badgeColor('gray') - ->modifyQueryUsing(fn (Builder $query) => $query->aborted()), + ->modifyQueryUsing(fn(Builder $query) => $query->aborted()), 'all' => Tab::make() ->label(__('all')), ]; } + + protected function getHeaderActions(): array + { + return [ + CreateAction::make() + ->icon('tabler-plus') + ->schema(ProjectResource::formFields(6, false)) + ->slideOver() + ->modalWidth(Width::Large), + ]; + } } diff --git a/app/Filament/Resources/ProjectResource/Widgets/CurrentProjects.php b/app/Filament/Resources/ProjectResource/Widgets/CurrentProjects.php index b1c499b..8edc762 100644 --- a/app/Filament/Resources/ProjectResource/Widgets/CurrentProjects.php +++ b/app/Filament/Resources/ProjectResource/Widgets/CurrentProjects.php @@ -11,7 +11,7 @@ class CurrentProjects extends Widget protected string $view = 'filament.widgets.current-projects-widget'; - protected int | string | array $columnSpan = [ + protected int|string|array $columnSpan = [ 'sm' => 12, 'xl' => 6, ]; diff --git a/app/Filament/Resources/SettingResource.php b/app/Filament/Resources/SettingResource.php index be8591e..10c38cb 100644 --- a/app/Filament/Resources/SettingResource.php +++ b/app/Filament/Resources/SettingResource.php @@ -12,7 +12,7 @@ class SettingResource extends Resource { protected static ?string $model = Setting::class; - protected static string | \BackedEnum | null $navigationIcon = 'tabler-adjustments'; + protected static string|\BackedEnum|null $navigationIcon = 'tabler-adjustments'; public static function table(Table $table): Table { @@ -21,7 +21,7 @@ public static function table(Table $table): Table ->columns([ TextColumn::make('field') ->label(__('field')) - ->state(fn (Setting $record): string => "{$record->label} ({$record->field})") + ->state(fn(Setting $record): string => "{$record->label} ({$record->field})") ->html(), TextInputColumn::make('value') ->label(__('value')) diff --git a/app/Filament/Widgets/ClientHoursChart.php b/app/Filament/Widgets/ClientHoursChart.php index 5c44911..5198432 100644 --- a/app/Filament/Widgets/ClientHoursChart.php +++ b/app/Filament/Widgets/ClientHoursChart.php @@ -13,7 +13,7 @@ class ClientHoursChart extends ChartWidget protected ?string $maxHeight = '180px'; protected ?string $pollingInterval = null; - protected int | string | array $columnSpan = [ + protected int|string|array $columnSpan = [ 'sm' => 12, 'xl' => 4, ]; @@ -49,7 +49,7 @@ protected function getData(): array $client = Client::find($clientId); $datasets[] = [ 'label' => $client->name, - 'data' => array_map(fn ($i) => $yearly[$i] ?? 0, array_keys($labels)), + 'data' => array_map(fn($i) => $yearly[$i] ?? 0, array_keys($labels)), 'fill' => 'start', 'borderColor' => $client->color, 'backgroundColor' => $client->color . '22', @@ -58,7 +58,7 @@ protected function getData(): array return [ 'datasets' => $datasets, - 'labels' => array_map(fn ($year) => (string) $year, $labels), + 'labels' => array_map(fn($year) => (string) $year, $labels), ]; } diff --git a/app/Filament/Widgets/ClientProfitChart.php b/app/Filament/Widgets/ClientProfitChart.php index 43a77de..86a3f88 100644 --- a/app/Filament/Widgets/ClientProfitChart.php +++ b/app/Filament/Widgets/ClientProfitChart.php @@ -13,7 +13,7 @@ class ClientProfitChart extends ChartWidget protected ?string $maxHeight = '180px'; protected ?string $pollingInterval = null; - protected int | string | array $columnSpan = [ + protected int|string|array $columnSpan = [ 'sm' => 12, 'xl' => 4, ]; @@ -49,7 +49,7 @@ protected function getData(): array $client = Client::find($clientId); $datasets[] = [ 'label' => $client->name, - 'data' => array_map(fn ($i) => $yearly[$i] ?? 0, array_keys($labels)), + 'data' => array_map(fn($i) => $yearly[$i] ?? 0, array_keys($labels)), 'fill' => 'start', 'borderColor' => $client->color, 'backgroundColor' => $client->color . '22', @@ -58,7 +58,7 @@ protected function getData(): array return [ 'datasets' => $datasets, - 'labels' => array_map(fn ($year) => (string) $year, $labels), + 'labels' => array_map(fn($year) => (string) $year, $labels), ]; } diff --git a/app/Filament/Widgets/ClientProfitDistributionChart.php b/app/Filament/Widgets/ClientProfitDistributionChart.php index 64bd82b..8074e1c 100644 --- a/app/Filament/Widgets/ClientProfitDistributionChart.php +++ b/app/Filament/Widgets/ClientProfitDistributionChart.php @@ -12,12 +12,12 @@ class ClientProfitDistributionChart extends ChartWidget { use HasEmptyStateChart; + public ?string $filter = ''; protected ?string $maxHeight = '180px'; - public ?string $filter = ''; protected ?string $pollingInterval = null; - protected int | string | array $columnSpan = [ + protected int|string|array $columnSpan = [ 'sm' => 12, 'xl' => 4, ]; @@ -48,8 +48,8 @@ protected function getData(): array : $obj->net; } $sum = array_sum($profit); - $labels = array_map(fn ($id, $p) => '(' . round($p/$sum*100, 1) . '%) ' . Client::find($id)->name, array_keys($profit), $profit); - $colors = array_map(fn ($id) => Client::find($id)->color, array_keys($profit)); + $labels = array_map(fn($id, $p) => '(' . round($p / $sum * 100, 1) . '%) ' . Client::find($id)->name, array_keys($profit), $profit); + $colors = array_map(fn($id) => Client::find($id)->color, array_keys($profit)); return [ 'datasets' => [ @@ -57,10 +57,10 @@ protected function getData(): array 'data' => array_values($profit), 'borderColor' => $colors, 'backgroundColor' => $colors, - 'hoverOffset' => 4 + 'hoverOffset' => 4, ], ], - 'labels' => $labels + 'labels' => $labels, ]; } diff --git a/app/Filament/Widgets/GiftSubjectDistributionChart.php b/app/Filament/Widgets/GiftSubjectDistributionChart.php index c5b231d..78c4724 100644 --- a/app/Filament/Widgets/GiftSubjectDistributionChart.php +++ b/app/Filament/Widgets/GiftSubjectDistributionChart.php @@ -11,12 +11,12 @@ class GiftSubjectDistributionChart extends ChartWidget { use HasEmptyStateChart; + public ?string $filter = 'all'; protected ?string $maxHeight = '180px'; - public ?string $filter = 'all'; protected ?string $pollingInterval = null; - protected int | string | array $columnSpan = [ + protected int|string|array $columnSpan = [ 'sm' => 12, 'xl' => 4, ]; @@ -49,8 +49,8 @@ protected function getData(): array : $obj->amount; } $sum = array_sum($amounts); - $labels = array_map(fn ($subject, $a) => '(' . round($a/$sum*100, 1) . '%) ' . $subject, array_keys($amounts), $amounts); - $colors = array_map(fn ($subject) => self::colorForSubject($subject), array_keys($amounts)); + $labels = array_map(fn($subject, $a) => '(' . round($a / $sum * 100, 1) . '%) ' . $subject, array_keys($amounts), $amounts); + $colors = array_map(fn($subject) => self::colorForSubject($subject), array_keys($amounts)); return [ 'datasets' => [ @@ -58,10 +58,10 @@ protected function getData(): array 'data' => array_values($amounts), 'borderColor' => $colors, 'backgroundColor' => $colors, - 'hoverOffset' => 4 + 'hoverOffset' => 4, ], ], - 'labels' => $labels + 'labels' => $labels, ]; } @@ -75,16 +75,6 @@ protected function getFilters(): ?array return ['all' => __('all')] + Gift::getYearList(); } - /** - * Deterministically derive a color from a subject name, so that - * the same subject always gets the same slice color across renders. - */ - private static function colorForSubject(string $subject): string - { - $hue = crc32($subject) % 360; - return "hsl({$hue}, 65%, 55%)"; - } - protected function getOptions(): RawJs { return RawJs::make(<< 12, 'xl' => 3, ]; @@ -38,19 +38,21 @@ protected function getData(): array $labels = iterator_to_array($period->map(fn(Carbon $date) => $date->format('Y'))); array_pop($labels); $period = $period->toArray(); - $count = array_fill(0, count($period)-1, 0); - $rates = array_fill(0, count($period)-1, 0); + $count = array_fill(0, count($period) - 1, 0); + $rates = array_fill(0, count($period) - 1, 0); foreach ($period as $i => $date) { - if ($i == count($period)-1) break; + if ($i == count($period) - 1) { + break; + } foreach ($invoices as $obj) { - if (CarbonPeriod::create($date, $period[$i+1])->contains($obj->paid_at)) { + if (CarbonPeriod::create($date, $period[$i + 1])->contains($obj->paid_at)) { $rates[$i] += $obj->net; $count[$i] += $obj->hours; } } } foreach ($rates as $i => $rate) { - $rates[$i] = $count[$i] != 0 ? round($rate/$count[$i]) : 0; + $rates[$i] = $count[$i] != 0 ? round($rate / $count[$i]) : 0; } return [ @@ -62,7 +64,7 @@ protected function getData(): array 'barPercentage' => 0.75, ], ], - 'labels' => $labels + 'labels' => $labels, ]; } diff --git a/app/Filament/Widgets/MonthlyIncomeChart.php b/app/Filament/Widgets/MonthlyIncomeChart.php index 71934cc..0a561f4 100644 --- a/app/Filament/Widgets/MonthlyIncomeChart.php +++ b/app/Filament/Widgets/MonthlyIncomeChart.php @@ -16,7 +16,7 @@ class MonthlyIncomeChart extends ChartWidget protected ?string $maxHeight = '180px'; protected ?string $pollingInterval = null; - protected int | string | array $columnSpan = [ + protected int|string|array $columnSpan = [ 'sm' => 12, 'xl' => 4, ]; @@ -45,12 +45,14 @@ protected function getData(): array $labels = iterator_to_array($period->map(fn(Carbon $date) => $date->format('Y'))); array_pop($labels); $period = $period->toArray(); - $invoiceData = array_fill(0, count($period)-1, 0); + $invoiceData = array_fill(0, count($period) - 1, 0); foreach ($period as $i => $date) { - if ($i == count($period)-1) break; + if ($i == count($period) - 1) { + break; + } foreach ($invoices as $obj) { - if (CarbonPeriod::create($date, $period[$i+1])->contains($obj->paid_at)) { - $invoiceData[$i] += match($this->filter) { + if (CarbonPeriod::create($date, $period[$i + 1])->contains($obj->paid_at)) { + $invoiceData[$i] += match ($this->filter) { 'net' => $obj->net, 'gross' => $obj->gross, }; @@ -59,23 +61,23 @@ protected function getData(): array if ($this->filter === 'net') { foreach ($taxes as $obj) { // Shift yearly income taxes post pays to the year before - if ($i > 0 && !Str($obj->description)->contains('EStVA') && CarbonPeriod::create($date, $period[$i+1])->contains(Carbon::parse($obj->expended_at))) { - $invoiceData[$i-1] = round($invoiceData[$i-1] - $obj->net/($i == count($period)-2 ? now()->month : 12), 2); + if ($i > 0 && !Str($obj->description)->contains('EStVA') && CarbonPeriod::create($date, $period[$i + 1])->contains(Carbon::parse($obj->expended_at))) { + $invoiceData[$i - 1] = round($invoiceData[$i - 1] - $obj->net / ($i == count($period) - 2 ? now()->month : 12), 2); continue; } // Handle income tax advance pays - if (CarbonPeriod::create($date, $period[$i+1])->contains(Carbon::parse($obj->expended_at))) { + if (CarbonPeriod::create($date, $period[$i + 1])->contains(Carbon::parse($obj->expended_at))) { $invoiceData[$i] -= $obj->net; } } } - $invoiceData[$i] = round($invoiceData[$i]/($i == count($period)-2 ? now()->month : 12), 2); + $invoiceData[$i] = round($invoiceData[$i] / ($i == count($period) - 2 ? now()->month : 12), 2); } return [ 'datasets' => [ [ - 'label' => match($this->filter) { + 'label' => match ($this->filter) { 'net' => __('netIncome'), 'gross' => __('grossIncome'), }, @@ -85,7 +87,7 @@ protected function getData(): array 'borderColor' => '#3b82f6', ], ], - 'labels' => $labels + 'labels' => $labels, ]; } diff --git a/app/Filament/Widgets/OfftimeChart.php b/app/Filament/Widgets/OfftimeChart.php index c1997c9..5f38946 100644 --- a/app/Filament/Widgets/OfftimeChart.php +++ b/app/Filament/Widgets/OfftimeChart.php @@ -11,11 +11,11 @@ class OfftimeChart extends ChartWidget { - protected ?string $maxHeight = '180px'; public ?string $filter = 'y'; + protected ?string $maxHeight = '180px'; protected ?string $pollingInterval = null; - protected int | string | array $columnSpan = [ + protected int|string|array $columnSpan = [ 'sm' => 12, 'xl' => 4, ]; @@ -38,22 +38,24 @@ protected function getData(): array array_pop($labels); $period = $period->toArray(); - $weekendData = array_fill(0, count($period)-1, 0); - $plannedData = array_fill(0, count($period)-1, 0); - $unplannedData = array_fill(0, count($period)-1, 0); - $totalData = array_fill(0, count($period)-1, 0); - $workedData = array_fill(0, count($period)-1, 0); + $weekendData = array_fill(0, count($period) - 1, 0); + $plannedData = array_fill(0, count($period) - 1, 0); + $unplannedData = array_fill(0, count($period) - 1, 0); + $totalData = array_fill(0, count($period) - 1, 0); + $workedData = array_fill(0, count($period) - 1, 0); foreach ($period as $i => $date) { - if ($i == count($period)-1) break; + if ($i == count($period) - 1) { + break; + } [$w, $p, $u, $t] = Offtime::daysCountByYear(intval($date->format('Y'))); $weekendData[$i] = $w; $plannedData[$i] = $p; $unplannedData[$i] = $u; $totalData[$i] = $t; - $workedData[$i] = Position::whereBetween('started_at', [$date, $period[$i+1]]) + $workedData[$i] = Position::whereBetween('started_at', [$date, $period[$i + 1]]) ->get(['started_at']) - ->map(fn ($obj) => $obj->started_at->format('Y-m-d')) + ->map(fn($obj) => $obj->started_at->format('Y-m-d')) ->unique() ->count(); } @@ -96,7 +98,7 @@ protected function getData(): array 'borderColor' => '#39444b', ], ], - 'labels' => $labels + 'labels' => $labels, ]; } diff --git a/app/Filament/Widgets/SalesChart.php b/app/Filament/Widgets/SalesChart.php index 2178c04..7dc0381 100644 --- a/app/Filament/Widgets/SalesChart.php +++ b/app/Filament/Widgets/SalesChart.php @@ -12,11 +12,11 @@ class SalesChart extends ChartWidget { - protected ?string $maxHeight = '180px'; public ?string $filter = 'y'; + protected ?string $maxHeight = '180px'; protected ?string $pollingInterval = null; - protected int | string | array $columnSpan = [ + protected int|string|array $columnSpan = [ 'sm' => 12, 'xl' => 5, ]; @@ -45,35 +45,37 @@ protected function getData(): array ->whereIn('category', ExpenseCategory::taxCategories()) ->oldest('expended_at') ->get(); - $period = match($this->filter) { + $period = match ($this->filter) { 'y' => Carbon::parse($invoices->first()?->paid_at)->startOfYear()->yearsUntil(now()->addYear()), 'q' => Carbon::parse($invoices->first()?->paid_at)->startOfQuarter()->quartersUntil(now()->addQuarter()), 'm' => Carbon::parse($invoices->first()?->paid_at)->startOfMonth()->monthsUntil(now()->addMonth()), }; - $labels = iterator_to_array($period->map(fn(Carbon $date) => match($this->filter) { + $labels = iterator_to_array($period->map(fn(Carbon $date) => match ($this->filter) { 'y' => $date->format('Y'), 'q' => $date->isoFormat('YYYY [Q]Q'), 'm' => $date->isoFormat('YYYY MMM'), })); array_pop($labels); $period = $period->toArray(); - $invoiceData = array_fill(0, count($period)-1, 0); - $expenseData = array_fill(0, count($period)-1, 0); - $taxData = array_fill(0, count($period)-1, 0); + $invoiceData = array_fill(0, count($period) - 1, 0); + $expenseData = array_fill(0, count($period) - 1, 0); + $taxData = array_fill(0, count($period) - 1, 0); foreach ($period as $i => $date) { - if ($i == count($period)-1) break; + if ($i == count($period) - 1) { + break; + } foreach ($invoices as $obj) { - if (CarbonPeriod::create($date, $period[$i+1])->contains($obj->paid_at)) { + if (CarbonPeriod::create($date, $period[$i + 1])->contains($obj->paid_at)) { $invoiceData[$i] += $obj->net; } } foreach ($expenses as $obj) { - if (CarbonPeriod::create($date, $period[$i+1])->contains($obj->expended_at)) { + if (CarbonPeriod::create($date, $period[$i + 1])->contains($obj->expended_at)) { $expenseData[$i] += $obj->net; } } foreach ($taxes as $obj) { - if (CarbonPeriod::create($date, $period[$i+1])->contains($obj->expended_at)) { + if (CarbonPeriod::create($date, $period[$i + 1])->contains($obj->expended_at)) { $taxData[$i] += $obj->net; } } @@ -103,7 +105,7 @@ protected function getData(): array 'borderColor' => '#f97316', ], ], - 'labels' => $labels + 'labels' => $labels, ]; } diff --git a/app/Filament/Widgets/StatsOverview.php b/app/Filament/Widgets/StatsOverview.php index 404da53..f995558 100644 --- a/app/Filament/Widgets/StatsOverview.php +++ b/app/Filament/Widgets/StatsOverview.php @@ -13,7 +13,7 @@ class StatsOverview extends BaseWidget { - protected int | string | array $columnSpan = [ + protected int|string|array $columnSpan = [ 'sm' => 12, 'xl' => 12, ]; @@ -21,16 +21,16 @@ class StatsOverview extends BaseWidget protected function getStats(): array { [$revenue, $hours] = $this->getData(); - $previousRevenue = $revenue[count($revenue)-2]; - $revenueStat = Number::currency($revenue[count($revenue)-1], 'eur'); - $revenueDiff = $revenue[count($revenue)-1] - $previousRevenue; + $previousRevenue = $revenue[count($revenue) - 2]; + $revenueStat = Number::currency($revenue[count($revenue) - 1], 'eur'); + $revenueDiff = $revenue[count($revenue) - 1] - $previousRevenue; $revenueIncrease = $revenueDiff >= 0; - $revenueDiffPercent = $previousRevenue ? round(abs($revenueDiff)/$previousRevenue*100) : 0; - $previousHours = $hours[count($hours)-2]; - $hoursStat = $hours[count($hours)-1]; - $hoursDiff = $hours[count($hours)-1] - $previousHours; + $revenueDiffPercent = $previousRevenue ? round(abs($revenueDiff) / $previousRevenue * 100) : 0; + $previousHours = $hours[count($hours) - 2]; + $hoursStat = $hours[count($hours) - 1]; + $hoursDiff = $hours[count($hours) - 1] - $previousHours; $hoursIncrease = $hoursDiff >= 0; - $hoursDiffPercent = $previousHours ? round(abs($hoursDiff)/$previousHours*100) : 0; + $hoursDiffPercent = $previousHours ? round(abs($hoursDiff) / $previousHours * 100) : 0; $vFilament = InstalledVersions::getPrettyVersion('filament/filament'); $vLaravel = InstalledVersions::getPrettyVersion('laravel/framework'); @@ -39,14 +39,14 @@ protected function getStats(): array Stat::make('weeklyRevenue', $revenueStat) ->label(__('weeklyRevenue')) ->description($revenueDiffPercent . '% ' . ($revenueIncrease ? __('increase') : __('decrease'))) - ->descriptionIcon($revenueIncrease ? 'tabler-trending-up': 'tabler-trending-down') + ->descriptionIcon($revenueIncrease ? 'tabler-trending-up' : 'tabler-trending-down') ->chart($revenue) ->color($revenueIncrease ? Color::Blue : Color::Red) ->extraAttributes(['class' => 'font-mono']), Stat::make('weeklyWorkingHours', Number::format($hoursStat)) ->label(__('weeklyWorkingHours')) ->description($hoursDiffPercent . '% ' . ($hoursIncrease ? __('increase') : __('decrease'))) - ->descriptionIcon($hoursIncrease ? 'tabler-trending-up': 'tabler-trending-down') + ->descriptionIcon($hoursIncrease ? 'tabler-trending-up' : 'tabler-trending-down') ->chart($hours) ->color($hoursIncrease ? Color::Blue : Color::Red) ->extraAttributes(['class' => 'font-mono']), @@ -60,12 +60,14 @@ protected function getData(): array { $positions = Position::where('started_at', '>', now()->subWeeks(7)->startOfWeek(Carbon::MONDAY))->get(); $period = now()->subWeeks(8)->startOfWeek()->weeksUntil(now()->addWeek()->endOfWeek(Carbon::SUNDAY))->toArray(); - $revenue = array_fill(0, count($period)-1, 0); - $hours = array_fill(0, count($period)-1, 0); + $revenue = array_fill(0, count($period) - 1, 0); + $hours = array_fill(0, count($period) - 1, 0); foreach ($period as $i => $date) { - if ($i == count($period)-1) break; + if ($i == count($period) - 1) { + break; + } foreach ($positions as $obj) { - if (CarbonPeriod::create($date, $period[$i+1])->contains($obj->started_at)) { + if (CarbonPeriod::create($date, $period[$i + 1])->contains($obj->started_at)) { $revenue[$i] += $obj->net; $hours[$i] += $obj->duration; } diff --git a/app/Filament/Widgets/SumGiftsChart.php b/app/Filament/Widgets/SumGiftsChart.php index 5d37ad7..6a9f503 100644 --- a/app/Filament/Widgets/SumGiftsChart.php +++ b/app/Filament/Widgets/SumGiftsChart.php @@ -12,12 +12,12 @@ class SumGiftsChart extends ChartWidget { use HasEmptyStateChart; + public ?string $filter = 'y'; protected ?string $maxHeight = '180px'; - public ?string $filter = 'y'; protected ?string $pollingInterval = null; - protected int | string | array $columnSpan = [ + protected int|string|array $columnSpan = [ 'sm' => 12, 'xl' => 4, ]; @@ -36,23 +36,25 @@ protected function getData(): array { $gifts = Gift::oldest('received_at')->get(); $start = $gifts->first()?->received_at; - $period = match($this->filter) { + $period = match ($this->filter) { 'y' => Carbon::parse($start)->startOfYear()->yearsUntil(now()->addYear()), 'q' => Carbon::parse($start)->startOfQuarter()->quartersUntil(now()->addQuarter()), 'm' => Carbon::parse($start)->startOfMonth()->monthsUntil(now()->addMonth()), }; - $labels = iterator_to_array($period->map(fn(Carbon $date) => match($this->filter) { + $labels = iterator_to_array($period->map(fn(Carbon $date) => match ($this->filter) { 'y' => $date->format('Y'), 'q' => $date->isoFormat('YYYY [Q]Q'), 'm' => $date->isoFormat('YYYY MMM'), })); array_pop($labels); $period = $period->toArray(); - $amounts = array_fill(0, count($period)-1, 0); + $amounts = array_fill(0, count($period) - 1, 0); foreach ($period as $i => $date) { - if ($i == count($period)-1) break; + if ($i == count($period) - 1) { + break; + } foreach ($gifts as $obj) { - if (CarbonPeriod::create($date, $period[$i+1])->contains($obj->received_at)) { + if (CarbonPeriod::create($date, $period[$i + 1])->contains($obj->received_at)) { $amounts[$i] += $obj->amount; } } @@ -67,7 +69,7 @@ protected function getData(): array 'barPercentage' => 0.75, ], ], - 'labels' => $labels + 'labels' => $labels, ]; } diff --git a/app/Filament/Widgets/SumProductiveHoursChart.php b/app/Filament/Widgets/SumProductiveHoursChart.php index 1469173..e4efb2a 100644 --- a/app/Filament/Widgets/SumProductiveHoursChart.php +++ b/app/Filament/Widgets/SumProductiveHoursChart.php @@ -10,11 +10,11 @@ class SumProductiveHoursChart extends ChartWidget { - protected ?string $maxHeight = '180px'; public ?string $filter = 'y'; + protected ?string $maxHeight = '180px'; protected ?string $pollingInterval = null; - protected int | string | array $columnSpan = [ + protected int|string|array $columnSpan = [ 'sm' => 12, 'xl' => 4, ]; @@ -33,23 +33,25 @@ protected function getData(): array { $positions = Position::oldest('started_at')->get(); $start = $positions->first()?->started_at; - $period = match($this->filter) { + $period = match ($this->filter) { 'y' => Carbon::parse($start)->startOfYear()->yearsUntil(now()->addYear()), 'q' => Carbon::parse($start)->startOfQuarter()->quartersUntil(now()->addQuarter()), 'm' => Carbon::parse($start)->startOfMonth()->monthsUntil(now()->addMonth()), }; - $labels = iterator_to_array($period->map(fn(Carbon $date) => match($this->filter) { + $labels = iterator_to_array($period->map(fn(Carbon $date) => match ($this->filter) { 'y' => $date->format('Y'), 'q' => $date->isoFormat('YYYY [Q]Q'), 'm' => $date->isoFormat('YYYY MMM'), })); array_pop($labels); $period = $period->toArray(); - $hours = array_fill(0, count($period)-1, 0); + $hours = array_fill(0, count($period) - 1, 0); foreach ($period as $i => $date) { - if ($i == count($period)-1) break; + if ($i == count($period) - 1) { + break; + } foreach ($positions as $obj) { - if (CarbonPeriod::create($date, $period[$i+1])->contains($obj->started_at)) { + if (CarbonPeriod::create($date, $period[$i + 1])->contains($obj->started_at)) { $hours[$i] += $obj->duration; } } @@ -65,7 +67,7 @@ protected function getData(): array 'borderColor' => '#3b82f6', ], ], - 'labels' => $labels + 'labels' => $labels, ]; } diff --git a/app/Filament/Widgets/TaxOverview.php b/app/Filament/Widgets/TaxOverview.php index f37533b..ef88b1d 100644 --- a/app/Filament/Widgets/TaxOverview.php +++ b/app/Filament/Widgets/TaxOverview.php @@ -20,10 +20,10 @@ class TaxOverview extends TableWidget implements HasActions { use InteractsWithActions; + public ?string $filter = 'm'; - protected int | string | array $columnSpan = 12; + protected int|string|array $columnSpan = 12; protected static int $entryCount = 14; - public ?string $filter = 'm'; public function lastAdvanceVatAction(): Action { @@ -61,18 +61,18 @@ public function table(Table $table): Table ->label(__('netTaxable')) ->money('eur') ->fontFamily(FontFamily::Mono) - ->color(fn (string $state): string => !$state ? 'gray' : 'primary') + ->color(fn(string $state): string => !$state ? 'gray' : 'primary') ->alignRight() ->copyable() - ->copyableState(fn (string $state): string => Number::format(floatval($state))), + ->copyableState(fn(string $state): string => Number::format(floatval($state))), TextColumn::make('netUntaxable') ->label(__('netUntaxable')) ->money('eur') ->fontFamily(FontFamily::Mono) - ->color(fn (string $state): string => !$state ? 'gray' : 'primary') + ->color(fn(string $state): string => !$state ? 'gray' : 'primary') ->alignRight() ->copyable() - ->copyableState(fn (string $state): string => Number::format(floatval($state))), + ->copyableState(fn(string $state): string => Number::format(floatval($state))), TextColumn::make('totalNet') ->label(__('netTotal')) ->money('eur') @@ -80,29 +80,29 @@ public function table(Table $table): Table ->color('gray') ->alignRight() ->copyable() - ->copyableState(fn (string $state): string => Number::format(floatval($state))), + ->copyableState(fn(string $state): string => Number::format(floatval($state))), TextColumn::make('vatExpenses') ->label(__('vatExpenses')) ->money('eur') ->fontFamily(FontFamily::Mono) - ->color(fn (string $state): string => !$state ? 'gray' : 'danger') + ->color(fn(string $state): string => !$state ? 'gray' : 'danger') ->alignRight() ->copyable() - ->copyableState(fn (string $state): string => Number::format(floatval($state))), + ->copyableState(fn(string $state): string => Number::format(floatval($state))), TextColumn::make('totalVat') ->label(__('totalVat')) ->money('eur') ->fontFamily(FontFamily::Mono) - ->color(fn (string $state): string => !$state ? 'gray' : 'normal') + ->color(fn(string $state): string => !$state ? 'gray' : 'normal') ->alignRight() ->copyable() - ->copyableState(fn (string $state): string => Number::format(floatval($state))), + ->copyableState(fn(string $state): string => Number::format(floatval($state))), ]); } public function getTableRecords(): Collection { - return match($this->filter) { + return match ($this->filter) { 'm' => $this->getMonthData(), 'q' => $this->getQuarterData(), 'y' => $this->getYearData(), @@ -114,7 +114,7 @@ private function getMonthData(): Collection $records = []; $dt = Carbon::today(); - for ($i=0; $i < static::$entryCount; $i++) { + for ($i = 0; $i < static::$entryCount; $i++) { [$netTaxable, $netUntaxable, $vatEarned] = Invoice::ofTime($dt, TimeUnit::MONTH); [, $vatExpended] = Expense::ofTime($dt, TimeUnit::MONTH); @@ -139,7 +139,7 @@ private function getQuarterData(): Collection $records = []; $dt = Carbon::today(); - for ($i=0; $i < static::$entryCount; $i++) { + for ($i = 0; $i < static::$entryCount; $i++) { [$netTaxable, $netUntaxable, $vatEarned] = Invoice::ofTime($dt, TimeUnit::QUARTER); [, $vatExpended] = Expense::ofTime($dt, TimeUnit::QUARTER); @@ -164,7 +164,7 @@ private function getYearData(): Collection $records = []; $dt = Carbon::today(); - for ($i=0; $i < static::$entryCount; $i++) { + for ($i = 0; $i < static::$entryCount; $i++) { [$netTaxable, $netUntaxable, $vatEarned] = Invoice::ofTime($dt, TimeUnit::YEAR); [, $vatExpended] = Expense::ofTime($dt, TimeUnit::YEAR); diff --git a/app/Filament/Widgets/TaxReturnFormInput.php b/app/Filament/Widgets/TaxReturnFormInput.php index 366f73c..a3dff70 100644 --- a/app/Filament/Widgets/TaxReturnFormInput.php +++ b/app/Filament/Widgets/TaxReturnFormInput.php @@ -16,8 +16,8 @@ class TaxReturnFormInput extends TableWidget { - protected int | string | array $columnSpan = 12; public ?int $filter = null; + protected int|string|array $columnSpan = 12; public function __construct() { @@ -37,15 +37,15 @@ public function table(Table $table): Table TextColumn::make('itr') ->label(__('itr')) ->fontFamily(FontFamily::Mono) - ->formatStateUsing(fn (?string $state) => $state ? __('lineN', ['n' => $state]) : ''), + ->formatStateUsing(fn(?string $state) => $state ? __('lineN', ['n' => $state]) : ''), TextColumn::make('vr') ->label(__('vr')) ->fontFamily(FontFamily::Mono) - ->formatStateUsing(fn (?string $state) => $state ? __('lineN', ['n' => $state]) : ''), + ->formatStateUsing(fn(?string $state) => $state ? __('lineN', ['n' => $state]) : ''), TextColumn::make('rsc') ->label(__('rsc')) ->fontFamily(FontFamily::Mono) - ->formatStateUsing(fn (?string $state) => $state ? __('lineN', ['n' => $state]) : ''), + ->formatStateUsing(fn(?string $state) => $state ? __('lineN', ['n' => $state]) : ''), TextColumn::make('help') ->color('gray') ->label(__('helpText')), @@ -56,7 +56,7 @@ public function table(Table $table): Table ->alignRight() ->color(fn(array $record) => $record['color'] ?? false) ->copyable() - ->copyableState(fn (string $state): string => Number::format(floatval($state))), + ->copyableState(fn(string $state): string => Number::format(floatval($state))), ]); } diff --git a/app/Filament/Widgets/WeeklyHoursChart.php b/app/Filament/Widgets/WeeklyHoursChart.php index 2d07622..c63cddd 100644 --- a/app/Filament/Widgets/WeeklyHoursChart.php +++ b/app/Filament/Widgets/WeeklyHoursChart.php @@ -13,7 +13,7 @@ class WeeklyHoursChart extends ChartWidget protected ?string $maxHeight = '180px'; protected ?string $pollingInterval = null; - protected int | string | array $columnSpan = [ + protected int|string|array $columnSpan = [ 'sm' => 12, 'xl' => 4, ]; @@ -35,20 +35,22 @@ protected function getData(): array $labels = iterator_to_array($period->map(fn(Carbon $date) => $date->format('Y'))); array_pop($labels); $period = $period->toArray(); - $countWeeks = array_fill(0, count($period)-1, 0); - $avgWeeklyHours = array_fill(0, count($period)-1, 0); + $countWeeks = array_fill(0, count($period) - 1, 0); + $avgWeeklyHours = array_fill(0, count($period) - 1, 0); foreach ($period as $i => $date) { - if ($i == count($period)-1) break; + if ($i == count($period) - 1) { + break; + } $weeks = []; $hours = 0; foreach ($positions as $obj) { - if (CarbonPeriod::create($date, $period[$i+1])->contains($obj->started_at)) { + if (CarbonPeriod::create($date, $period[$i + 1])->contains($obj->started_at)) { $weeks[Carbon::parse($obj->started_at)->isoWeek()] = 1; $hours += $obj->duration; } } $countWeeks[$i] = count($weeks); - $avgWeeklyHours[$i] = count($weeks) != 0 ? round($hours/count($weeks)) : 0; + $avgWeeklyHours[$i] = count($weeks) != 0 ? round($hours / count($weeks)) : 0; } return [ @@ -70,7 +72,7 @@ protected function getData(): array 'yAxisID' => 'y2', ], ], - 'labels' => $labels + 'labels' => $labels, ]; } diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 77ec359..f1406be 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -8,5 +8,6 @@ class Controller extends BaseController { - use AuthorizesRequests, ValidatesRequests; + use AuthorizesRequests; + use ValidatesRequests; } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 8be3302..13e9456 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -63,7 +63,7 @@ class Kernel extends HttpKernel 'api' => [ // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, - ThrottleRequests::class.':api', + ThrottleRequests::class . ':api', SubstituteBindings::class, ], ]; diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php index 3391630..e53eb4d 100644 --- a/app/Http/Middleware/TrustProxies.php +++ b/app/Http/Middleware/TrustProxies.php @@ -19,10 +19,10 @@ class TrustProxies extends Middleware * * @var int */ - protected $headers = - Request::HEADER_X_FORWARDED_FOR | - Request::HEADER_X_FORWARDED_HOST | - Request::HEADER_X_FORWARDED_PORT | - Request::HEADER_X_FORWARDED_PROTO | - Request::HEADER_X_FORWARDED_AWS_ELB; + protected $headers + = Request::HEADER_X_FORWARDED_FOR + | Request::HEADER_X_FORWARDED_HOST + | Request::HEADER_X_FORWARDED_PORT + | Request::HEADER_X_FORWARDED_PROTO + | Request::HEADER_X_FORWARDED_AWS_ELB; } diff --git a/app/Livewire/HeadingWidget.php b/app/Livewire/HeadingWidget.php index 30dbfd5..554e053 100644 --- a/app/Livewire/HeadingWidget.php +++ b/app/Livewire/HeadingWidget.php @@ -10,7 +10,7 @@ class HeadingWidget extends Widget protected string $view = 'livewire.heading-widget'; - protected int | string | array $columnSpan = 12; + protected int|string|array $columnSpan = 12; protected function getViewData(): array { diff --git a/app/Mail/ContactClient.php b/app/Mail/ContactClient.php index 5d50c4d..f3e6d2b 100644 --- a/app/Mail/ContactClient.php +++ b/app/Mail/ContactClient.php @@ -11,7 +11,8 @@ class ContactClient extends Mailable { - use Queueable, SerializesModels; + use Queueable; + use SerializesModels; /** * Create a new message instance. diff --git a/app/Models/Client.php b/app/Models/Client.php index 3da0664..27da24a 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -29,6 +29,22 @@ class Client extends Model 'vat_id', ]; + /** + * The projects this client ordered. + */ + public function projects(): HasMany + { + return $this->hasMany(Project::class); + } + + /** + * The projects this client ordered. + */ + public function invoices(): HasManyThrough + { + return $this->hasManyThrough(Invoice::class, Project::class); + } + protected function casts(): array { return [ @@ -49,22 +65,6 @@ protected function casts(): array ]; } - /** - * The projects this client ordered. - */ - public function projects(): HasMany - { - return $this->hasMany(Project::class); - } - - /** - * The projects this client ordered. - */ - public function invoices(): HasManyThrough - { - return $this->hasManyThrough(Invoice::class, Project::class); - } - /** * All address information of this client as one string */ @@ -117,6 +117,6 @@ protected function avgPaymentDelay(): Attribute } } } - return Attribute::make(fn(): float => count($days) ? array_sum($days)/count($days) : 0.0); + return Attribute::make(fn(): float => count($days) ? array_sum($days) / count($days) : 0.0); } } diff --git a/app/Models/Estimate.php b/app/Models/Estimate.php index 0fcf085..3c41735 100644 --- a/app/Models/Estimate.php +++ b/app/Models/Estimate.php @@ -17,6 +17,14 @@ class Estimate extends Model 'weight', ]; + /** + * Get the client that ordered the project. + */ + public function project(): BelongsTo + { + return $this->belongsTo(Project::class); + } + protected function casts(): array { return [ @@ -28,12 +36,4 @@ protected function casts(): array 'updated_at' => 'datetime', ]; } - - /** - * Get the client that ordered the project. - */ - public function project(): BelongsTo - { - return $this->belongsTo(Project::class); - } } diff --git a/app/Models/Expense.php b/app/Models/Expense.php index 4406835..7141304 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -23,6 +23,53 @@ class Expense extends Model 'description', ]; + public static function lastAdvanceVatExists(): bool + { + $format = 'UStVA ' . now()->year . '-' . now()->subMonth()->isoFormat('MM'); + return self::where('description', $format)->first() !== null; + } + + public static function saveLastAdvanceVat(): bool + { + [,, $vatIn] = Invoice::ofTime(now()->subMonth(), TimeUnit::MONTH); + [, $vatOut] = self::ofTime(now()->subMonth(), TimeUnit::MONTH); + $obj = new self([ + 'expended_at' => now(), + 'category' => ExpenseCategory::Vat, + 'price' => $vatIn - $vatOut, + 'quantity' => 1, + 'taxable' => false, + 'vat_rate' => 0, + 'description' => 'UStVA ' . now()->year . '-' . now()->subMonth()->isoFormat('MM'), + ]); + return $obj->save(); + } + + /** + * Sum of net and vat amounts of given time range + */ + public static function ofTime(Carbon $d, TimeUnit $u, ?ExpenseCategory $category = null): array + { + $start = match ($u) { + TimeUnit::MONTH => $d->startOfMonth()->toDateString(), + TimeUnit::QUARTER => $d->startOfQuarter()->toDateString(), + TimeUnit::YEAR => $d->startOfYear()->toDateString(), + }; + $end = match ($u) { + TimeUnit::MONTH => $d->endOfMonth()->toDateString(), + TimeUnit::QUARTER => $d->endOfQuarter()->toDateString(), + TimeUnit::YEAR => $d->endOfYear()->toDateString(), + }; + $categories = $category ? [$category] : ExpenseCategory::deliverableCategories(); + $records = self::where('expended_at', '>=', $start) + ->where('expended_at', '<=', $end) + ->whereIn('category', $categories) + ->get(); + $net = array_sum($records->map(fn(self $r) => $r->net)->toArray()); + $vat = array_sum($records->map(fn(self $r) => $r->vat)->toArray()); + return [$net, $vat]; + } + protected function casts(): array { return [ @@ -70,51 +117,4 @@ protected function vat(): Attribute { return Attribute::make(fn(): float => round($this->gross - $this->net, 2)); } - - public static function lastAdvanceVatExists(): bool - { - $format = 'UStVA ' . now()->year . '-' . now()->subMonth()->isoFormat('MM'); - return self::where('description', $format)->first() !== null; - } - - public static function saveLastAdvanceVat(): bool - { - [,, $vatIn] = Invoice::ofTime(now()->subMonth(), TimeUnit::MONTH); - [, $vatOut] = self::ofTime(now()->subMonth(), TimeUnit::MONTH); - $obj = new self([ - 'expended_at' => now(), - 'category' => ExpenseCategory::Vat, - 'price' => $vatIn - $vatOut, - 'quantity' => 1, - 'taxable' => false, - 'vat_rate' => 0, - 'description' => 'UStVA ' . now()->year . '-' . now()->subMonth()->isoFormat('MM'), - ]); - return $obj->save(); - } - - /** - * Sum of net and vat amounts of given time range - */ - public static function ofTime(Carbon $d, TimeUnit $u, ?ExpenseCategory $category = null): array - { - $start = match ($u) { - TimeUnit::MONTH => $d->startOfMonth()->toDateString(), - TimeUnit::QUARTER => $d->startOfQuarter()->toDateString(), - TimeUnit::YEAR => $d->startOfYear()->toDateString(), - }; - $end = match ($u) { - TimeUnit::MONTH => $d->endOfMonth()->toDateString(), - TimeUnit::QUARTER => $d->endOfQuarter()->toDateString(), - TimeUnit::YEAR => $d->endOfYear()->toDateString(), - }; - $categories = $category ? [$category] : ExpenseCategory::deliverableCategories(); - $records = self::where('expended_at', '>=', $start) - ->where('expended_at', '<=', $end) - ->whereIn('category', $categories) - ->get(); - $net = array_sum($records->map(fn (self $r) => $r->net)->toArray()); - $vat = array_sum($records->map(fn (self $r) => $r->vat)->toArray()); - return [$net, $vat]; - } } diff --git a/app/Models/Gift.php b/app/Models/Gift.php index 9b3f69e..1e0a0c6 100644 --- a/app/Models/Gift.php +++ b/app/Models/Gift.php @@ -18,19 +18,6 @@ class Gift extends Model 'email', ]; - protected function casts(): array - { - return [ - 'received_at' => 'date', - 'amount' => 'float', - 'subject' => 'string', - 'name' => 'string', - 'email' => 'string', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - ]; - } - /** * Calculate array holding all years having gifts * sorted from current to past @@ -43,9 +30,22 @@ public static function getYearList(): array $period = Carbon::parse($firstDate)->startOfYear()->yearsUntil(now()); $years = array_reverse( iterator_to_array( - $period->map(fn(Carbon $date) => $date->format('Y')) - ) + $period->map(fn(Carbon $date) => $date->format('Y')), + ), ); return array_combine($years, $years); } + + protected function casts(): array + { + return [ + 'received_at' => 'date', + 'amount' => 'float', + 'subject' => 'string', + 'name' => 'string', + 'email' => 'string', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + } } diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index c5a6861..e94f65f 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -14,7 +14,6 @@ use Illuminate\Support\Number; use Carbon\Carbon; use Illuminate\Database\Eloquent\Casts\Attribute; -use Illuminate\Support\Collection; class Invoice extends Model { @@ -35,6 +34,65 @@ class Invoice extends Model 'deduction', ]; + /** + * Get the project this invoice is assigned to. + */ + public function project(): BelongsTo + { + return $this->belongsTo(Project::class); + } + + /** + * The positions of this invoice. + */ + public function positions(): HasMany + { + return $this->hasMany(Position::class); + } + + /** + * Calculate array holding all years having paid invoices + * sorted from current to past + * + * @return array + */ + public static function getYearList(): array + { + $firstDate = self::whereNotNull('paid_at') + ->where('transitory', 0) + ->oldest('paid_at') + ->first()?->paid_at; + $period = Carbon::parse($firstDate)->startOfYear()->yearsUntil(now()); + $years = array_reverse( + iterator_to_array( + $period->map(fn(Carbon $date) => $date->format('Y')), + ), + ); + return array_combine($years, $years); + } + + /** + * Sum of net and vat amounts of given time range + */ + public static function ofTime(Carbon $d, TimeUnit $u): array + { + $start = match ($u) { + TimeUnit::MONTH => $d->startOfMonth()->toDateString(), + TimeUnit::QUARTER => $d->startOfQuarter()->toDateString(), + TimeUnit::YEAR => $d->startOfYear()->toDateString(), + }; + $end = match ($u) { + TimeUnit::MONTH => $d->endOfMonth()->toDateString(), + TimeUnit::QUARTER => $d->endOfQuarter()->toDateString(), + TimeUnit::YEAR => $d->endOfYear()->toDateString(), + }; + $records = self::where('paid_at', '>=', $start)->where('paid_at', '<=', $end)->get(); + $netTaxable = $records->filter(fn(self $r) => $r->taxable)->map(fn(self $r) => $r->net)->sum(); + $netUntaxable = $records->filter(fn(self $r) => !$r->taxable)->map(fn(self $r) => $r->net)->sum(); + $vat = $records->map(fn(self $r) => $r->vat)->sum(); + return [$netTaxable, $netUntaxable, $vat]; + } + protected function casts(): array { return [ @@ -55,22 +113,6 @@ protected function casts(): array ]; } - /** - * Get the project this invoice is assigned to. - */ - public function project(): BelongsTo - { - return $this->belongsTo(Project::class); - } - - /** - * The positions of this invoice. - */ - public function positions(): HasMany - { - return $this->hasMany(Position::class); - } - /** * Scope a query to only include active invoices. */ @@ -105,7 +147,7 @@ protected function sortedPositions(): Attribute { // If undated, sort by positions creation date, if dated, sort by positions starting date return Attribute::make( - get: fn(): array => $this->positions->sortBy($this->undated ? 'created_at' : 'started_at')->all() + get: fn(): array => $this->positions->sortBy($this->undated ? 'created_at' : 'started_at')->all(), ); } @@ -121,8 +163,8 @@ protected function paginatedPositions(): Attribute // Take the description lines and the position title (2 lines) into account $lineCount = count(explode("\n", trim($p->description))) + 2; $linesProcessed += $lineCount; - $i = intval(floor($linesProcessed/50)); - if (key_exists($i,$paginated)) { + $i = intval(floor($linesProcessed / 50)); + if (key_exists($i, $paginated)) { $paginated[$i][] = $p; } else { $paginated[$i] = [$p]; @@ -137,7 +179,7 @@ protected function paginatedPositions(): Attribute protected function positionsFormatted(): Attribute { return Attribute::make( - fn(): string => count($this->positions) . ' ' . trans_choice('position', count($this->positions)) + fn(): string => count($this->positions) . ' ' . trans_choice('position', count($this->positions)), ); } @@ -256,47 +298,4 @@ protected function status(): Attribute default => InvoiceStatus::INVALID, }); } - - /** - * Calculate array holding all years having paid invoices - * sorted from current to past - * - * @return array - */ - public static function getYearList(): array - { - $firstDate = self::whereNotNull('paid_at') - ->where('transitory', 0) - ->oldest('paid_at') - ->first()?->paid_at; - $period = Carbon::parse($firstDate)->startOfYear()->yearsUntil(now()); - $years = array_reverse( - iterator_to_array( - $period->map(fn(Carbon $date) => $date->format('Y')) - ) - ); - return array_combine($years, $years); - } - - /** - * Sum of net and vat amounts of given time range - */ - public static function ofTime(Carbon $d, TimeUnit $u): array - { - $start = match ($u) { - TimeUnit::MONTH => $d->startOfMonth()->toDateString(), - TimeUnit::QUARTER => $d->startOfQuarter()->toDateString(), - TimeUnit::YEAR => $d->startOfYear()->toDateString(), - }; - $end = match ($u) { - TimeUnit::MONTH => $d->endOfMonth()->toDateString(), - TimeUnit::QUARTER => $d->endOfQuarter()->toDateString(), - TimeUnit::YEAR => $d->endOfYear()->toDateString(), - }; - $records = self::where('paid_at', '>=', $start)->where('paid_at', '<=', $end)->get(); - $netTaxable = $records->filter(fn (self $r) => $r->taxable)->map(fn (self $r) => $r->net)->sum(); - $netUntaxable = $records->filter(fn (self $r) => !$r->taxable)->map(fn (self $r) => $r->net)->sum(); - $vat = $records->map(fn (self $r) => $r->vat)->sum(); - return [$netTaxable, $netUntaxable, $vat]; - } } diff --git a/app/Models/Offtime.php b/app/Models/Offtime.php index af84434..cdb7a45 100644 --- a/app/Models/Offtime.php +++ b/app/Models/Offtime.php @@ -21,32 +21,6 @@ class Offtime extends Model 'description', ]; - protected function casts(): array - { - return [ - 'start' => 'date', - 'end' => 'date', - 'category' => OfftimeCategory::class, - 'description' => 'string', - ]; - } - - /** - * Year the time off is assigned to - */ - protected function year(): Attribute - { - return Attribute::make(fn(): int => intval($this->start->format('Y'))); - } - - /** - * Number of days this off time consists of - */ - protected function daysCount(): Attribute - { - return Attribute::make(fn(): int => $this->end ? intval($this->start->diffInDays($this->end)) + 1 : 1); - } - /** * Get a time off on a given date or null if none exists */ @@ -106,5 +80,31 @@ public static function daysCountByYear(int $year): array return [$weekends, $cat['planned'], $cat['unplanned'], $total->count()]; } + protected function casts(): array + { + return [ + 'start' => 'date', + 'end' => 'date', + 'category' => OfftimeCategory::class, + 'description' => 'string', + ]; + } + + /** + * Year the time off is assigned to + */ + protected function year(): Attribute + { + return Attribute::make(fn(): int => intval($this->start->format('Y'))); + } + + /** + * Number of days this off time consists of + */ + protected function daysCount(): Attribute + { + return Attribute::make(fn(): int => $this->end ? intval($this->start->diffInDays($this->end)) + 1 : 1); + } + } diff --git a/app/Models/Position.php b/app/Models/Position.php index 9b7add8..0a5d0c4 100644 --- a/app/Models/Position.php +++ b/app/Models/Position.php @@ -21,6 +21,14 @@ class Position extends Model 'remote', ]; + /** + * Get the invoice this position was made for. + */ + public function invoice(): BelongsTo + { + return $this->belongsTo(Invoice::class); + } + protected function casts(): array { return [ @@ -34,14 +42,6 @@ protected function casts(): array ]; } - /** - * Get the invoice this position was made for. - */ - public function invoice(): BelongsTo - { - return $this->belongsTo(Invoice::class); - } - /** * Total duration of the position in hours */ @@ -49,7 +49,7 @@ protected function duration(): Attribute { return Attribute::make( fn(): float => Carbon::parse($this->started_at) - ->diffInMinutes(Carbon::parse($this->finished_at))/60 - $this->pause_duration + ->diffInMinutes(Carbon::parse($this->finished_at)) / 60 - $this->pause_duration, ); } @@ -64,7 +64,7 @@ protected function net(): Attribute $net = 0; if ($this->invoice->pricing_unit === PricingUnit::Project) { - $net = $this->invoice->hours/$this->invoice->net * $this->duration; + $net = $this->invoice->hours / $this->invoice->net * $this->duration; } else { $net += $this->duration * $this->invoice->price / $this->invoice->pricing_hours; } @@ -78,7 +78,7 @@ protected function timeRange(): Attribute { return Attribute::make( fn(): string => Carbon::parse($this->started_at)->isoFormat('lll') - . Carbon::parse($this->finished_at)->format(' - H.i') + . Carbon::parse($this->finished_at)->format(' - H.i'), ); } } diff --git a/app/Models/Project.php b/app/Models/Project.php index 3d5edf5..1764f8c 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -3,7 +3,6 @@ namespace App\Models; use App\Enums\PricingUnit; -use App\Models\Setting; use Illuminate\Contracts\Support\Htmlable; use Illuminate\Database\Eloquent\Attributes\Scope; use Illuminate\Database\Eloquent\Builder; @@ -30,23 +29,6 @@ class Project extends Model 'aborted', ]; - protected function casts(): array - { - return [ - 'title' => 'string', - 'description' => 'string', - 'start_at' => 'date', - 'due_at' => 'date', - 'minimum' => 'float', - 'scope' => 'float', - 'price' => 'float', - 'pricing_unit' => PricingUnit::class, - 'aborted' => 'bool', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - ]; - } - /** * Get the client that ordered the project. */ @@ -71,6 +53,23 @@ public function invoices(): HasMany return $this->hasMany(Invoice::class); } + protected function casts(): array + { + return [ + 'title' => 'string', + 'description' => 'string', + 'start_at' => 'date', + 'due_at' => 'date', + 'minimum' => 'float', + 'scope' => 'float', + 'price' => 'float', + 'pricing_unit' => PricingUnit::class, + 'aborted' => 'bool', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + } + /** * Scope a query to only include active projects. */ @@ -128,8 +127,8 @@ protected function paginatedEstimates(): Attribute // Take the description lines and the position title (2 lines) into account $lineCount = count(explode("\n", trim($e->description))) + 2; $linesProcessed += $lineCount; - $i = intval(floor($linesProcessed/50)); - if (key_exists($i,$paginated)) { + $i = intval(floor($linesProcessed / 50)); + if (key_exists($i, $paginated)) { $paginated[$i][] = $e; } else { $paginated[$i] = [$e]; @@ -177,8 +176,8 @@ protected function scopeRange(): Attribute { return Attribute::make( fn(): string => $this->minimum != $this->scope - ? (int)$this->minimum . ' - ' . (int)$this->scope . ' ' . trans_choice('hour', 2) - : (int)$this->scope . ' ' . trans_choice('hour', (int)$this->scope) + ? (int) $this->minimum . ' - ' . (int) $this->scope . ' ' . trans_choice('hour', 2) + : (int) $this->scope . ' ' . trans_choice('hour', (int) $this->scope), ); } @@ -187,7 +186,7 @@ protected function scopeRange(): Attribute */ protected function pricePerUnit(): Attribute { - return Attribute::make(fn(): string => $this->price . ' € / ' . match($this->pricing_unit) { + return Attribute::make(fn(): string => $this->price . ' € / ' . match ($this->pricing_unit) { PricingUnit::Hour => trans_choice('hour', 1), PricingUnit::Day => trans_choice('day', 1), PricingUnit::Project => trans_choice('project', 1), @@ -201,8 +200,8 @@ protected function progress(): Attribute { return Attribute::make( fn(): float => $this->scope > 0 - ? round($this->hours/$this->scope*100, 1) - : 0.0 + ? round($this->hours / $this->scope * 100, 1) + : 0.0, ); } @@ -214,7 +213,7 @@ protected function progressPercent(): Attribute return Attribute::make( fn(): string => $this->scope > 0 ? strval($this->progress) . ' %' - : __('n/a') + : __('n/a'), ); } diff --git a/app/Models/Setting.php b/app/Models/Setting.php index 1b96beb..762a7a7 100644 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -7,9 +7,8 @@ class Setting extends Model { - protected $primaryKey = 'field'; - public $incrementing = false; + protected $primaryKey = 'field'; protected $fillable = [ 'field', @@ -19,6 +18,23 @@ class Setting extends Model 'weight', ]; + public static function get(string $field) + { + return self::find($field)?->value; + } + + /** + * Address field + */ + public static function address() + { + $name = self::get('name'); + $street = self::get('street'); + $zip = self::get('zip'); + $city = self::get('city'); + return "$name, $street, $zip $city"; + } + protected function casts(): array { return [ @@ -32,10 +48,6 @@ protected function casts(): array ]; } - public static function get(string $field) { - return self::find($field)?->value; - } - /** * Translated setting label */ @@ -43,16 +55,4 @@ protected function label(): Attribute { return Attribute::make(fn(): string => __($this->field)); } - - /** - * Address field - */ - public static function address() - { - $name = self::get('name'); - $street = self::get('street'); - $zip = self::get('zip'); - $city = self::get('city'); - return "$name, $street, $zip $city"; - } } diff --git a/app/Models/User.php b/app/Models/User.php index 7e1b842..5764488 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -12,7 +12,9 @@ class User extends Authenticatable implements FilamentUser, HasAvatar { - use HasApiTokens, HasFactory, Notifiable; + use HasApiTokens; + use HasFactory; + use Notifiable; /** * The attributes that are mass assignable. @@ -31,21 +33,6 @@ class User extends Authenticatable implements FilamentUser, HasAvatar 'remember_token', ]; - /** - * The attributes that should be cast. - */ - protected function casts(): array - { - return [ - 'name' => 'string', - 'email' => 'string', - 'email_verified_at' => 'datetime', - 'password' => 'hashed', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - ]; - } - public function canAccessPanel(Panel $panel): bool { // TODO: limit to defined domains @@ -63,4 +50,19 @@ public function getFilamentAvatarUrl(): ?string HTML; } + + /** + * The attributes that should be cast. + */ + protected function casts(): array + { + return [ + 'name' => 'string', + 'email' => 'string', + 'email_verified_at' => 'datetime', + 'password' => 'hashed', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + } } diff --git a/app/Services/InvoiceService.php b/app/Services/InvoiceService.php index f1c63a8..912d6b9 100644 --- a/app/Services/InvoiceService.php +++ b/app/Services/InvoiceService.php @@ -24,7 +24,8 @@ class InvoiceService * @param Invoice $invoice Record to export * @return string */ - public static function generatePdf(Invoice $invoice): string { + public static function generatePdf(Invoice $invoice): string + { $conf = Setting::pluck('value', 'field'); $client = $invoice?->project?->client; $lang = $client?->language->value ?? 'de'; @@ -46,7 +47,7 @@ public static function generatePdf(Invoice $invoice): string { 'title' => $invoice->title, 'vat' => Number::currency($invoice->vat, 'EUR', locale: $lang), 'vatRate' => $invoice->taxable - ? Number::percentage($invoice->vat_rate*100, 2, locale: $lang) . ' ' . __("vat", locale: $lang) + ? Number::percentage($invoice->vat_rate * 100, 2, locale: $lang) . ' ' . __("vat", locale: $lang) : __('vatNotChargeable', locale: $lang), ]); @@ -84,9 +85,9 @@ public static function generatePdf(Invoice $invoice): string { ]); // Convert to supported char encoding - $conf = $conf->map(fn ($e) => iconv('UTF-8', 'windows-1252', $e)); - $data = $data->map(fn ($e) => iconv('UTF-8', 'windows-1252', $e)); - $label = $label->map(fn ($e) => iconv('UTF-8', 'windows-1252', $e)); + $conf = $conf->map(fn($e) => iconv('UTF-8', 'windows-1252', $e)); + $data = $data->map(fn($e) => iconv('UTF-8', 'windows-1252', $e)); + $label = $label->map(fn($e) => iconv('UTF-8', 'windows-1252', $e)); // Init document $pdf = new PdfTemplate($lang); @@ -221,7 +222,8 @@ public static function generatePdf(Invoice $invoice): string { $rowHeight = 3.5; $totalHeight = array_reduce( $positions, - fn ($a, $c) => $a + count(explode("\n", $c->description)) + 2, 0 + fn($a, $c) => $a + count(explode("\n", $c->description)) + 2, + 0, ) * $rowHeight + 32; $pdf->addPage(); @@ -278,7 +280,7 @@ public static function generatePdf(Invoice $invoice): string { ]); // Convert to supported char encoding - $posdata = $posdata->map(fn ($e) => iconv('UTF-8', 'windows-1252', $e)); + $posdata = $posdata->map(fn($e) => iconv('UTF-8', 'windows-1252', $e)); $pdf->setTextColor(Color::DARK->pdfColor()) ->setFont('FiraSans-Regular') @@ -316,7 +318,8 @@ public static function generatePdf(Invoice $invoice): string { * @param Invoice $invoice Record to export * @return string */ - public static function generateEn16931Xml(Invoice $invoice): string { + public static function generateEn16931Xml(Invoice $invoice): string + { $settings = Setting::pluck('value', 'field'); $client = $invoice?->project?->client; $lang = $client?->language->value ?? 'de'; @@ -330,175 +333,175 @@ public static function generateEn16931Xml(Invoice $invoice): string { $x->setIndent(true); $x->setIndentString(' '); $x->startDocument('1.0', 'UTF-8'); - $x->startElement('Invoice'); - $x->writeAttribute('xmlns:cac', 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'); - $x->writeAttribute('xmlns:cbc', 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'); - $x->writeAttribute('xmlns:qdt', 'urn:oasis:names:specification:ubl:schema:xsd:QualifiedDataTypes-2'); - $x->writeAttribute('xmlns:udt', 'urn:oasis:names:specification:ubl:schema:xsd:UnqualifiedDataTypes-2'); - $x->writeAttribute('xmlns:ccts', 'urn:un:unece:uncefact:documentation:2'); - $x->writeAttribute('xmlns', 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'); - $x->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); - $x->writeAttribute('xsi:schemaLocation', 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2 http://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-Invoice-2.1.xsd'); - // Meta - $x->writeElement('cbc:CustomizationID', 'urn:cen.eu:en16931:2017'); - $x->writeElement('cbc:ID', $invoice->current_number); - $x->writeElement('cbc:IssueDate', Carbon::now()->format('Y-m-d')); - $x->writeElement('cbc:DueDate', Carbon::now()->addWeeks(2)->format('Y-m-d')); - // 380 Rechnung - // 381 Gutschrift - // 384 Rechnungskorrektur - $x->writeElement('cbc:InvoiceTypeCode', 380); - $x->writeElement('cbc:DocumentCurrencyCode', $currency); - // Contractor (me) - $x->startElement('cac:AccountingSupplierParty'); - $x->startElement('cac:Party'); - $x->startElement('cac:PostalAddress'); - $x->writeElement('cbc:StreetName', $settings['street']); - $x->writeElement('cbc:CityName', $settings['city']); - $x->writeElement('cbc:PostalZone', $settings['zip']); - $x->startElement('cac:Country'); - $x->writeElement('cbc:IdentificationCode', 'DE'); // TODO: $settings['country'] - $x->endElement(); - $x->endElement(); - $x->startElement('cac:PartyTaxScheme'); - $x->writeElement('cbc:CompanyID', $settings['vatId']); - $x->startElement('cac:TaxScheme'); - $x->writeElement('cbc:ID', 'VAT'); - $x->endElement(); - $x->endElement(); - $x->startElement('cac:PartyLegalEntity'); - $x->writeElement('cbc:RegistrationName', $settings['name']); - $x->writeElement('cbc:CompanyID', $settings['vatId']); - $x->endElement(); - $x->startElement('cac:Contact'); - $x->writeElement('cbc:ElectronicMail', $settings['email']); - $x->endElement(); - $x->endElement(); - $x->endElement(); - // Client - $x->startElement('cac:AccountingCustomerParty'); - $x->startElement('cac:Party'); - $x->startElement('cac:PostalAddress'); - $x->writeElement('cbc:StreetName', $client?->street); - $x->writeElement('cbc:CityName', $client?->city); - $x->writeElement('cbc:PostalZone', $client?->zip); - $x->startElement('cac:Country'); - $x->writeElement('cbc:IdentificationCode', $client?->country); - $x->endElement(); - $x->endElement(); - $x->startElement('cac:PartyTaxScheme'); - if ($client?->vat_id) { - $x->writeElement('cbc:CompanyID', $client->vat_id); - } - $x->startElement('cac:TaxScheme'); - $x->writeElement('cbc:ID', 'VAT'); - $x->endElement(); - $x->endElement(); - $x->startElement('cac:PartyLegalEntity'); - $x->writeElement('cbc:RegistrationName', $client?->name); - if ($client?->vat_id) { - $x->writeElement('cbc:CompanyID', $client->vat_id); - } - $x->endElement(); - $x->endElement(); - $x->endElement(); - // Payment - $x->startElement('cac:PaymentMeans'); - // 58 SEPA credit transfer - // 59 SEPA direct debit - // 57 Standing agreement - // 30 Credit transfer (non-SEPA) - // 49 Direct debit (non-SEPA) - // 48 Bank card - $x->writeElement('cbc:PaymentMeansCode', 30); - $x->writeElement('cbc:PaymentID', $invoice->current_number); - $x->startElement('cac:PayeeFinancialAccount'); - $x->writeElement('cbc:ID', $settings['iban']); - $x->endElement(); - $x->endElement(); - // Tax - $x->startElement('cac:TaxTotal'); - $x->startElement('cbc:TaxAmount'); - $x->writeAttribute('currencyID', $currency); - $x->text($invoice->vat); - $x->endElement(); - $x->startElement('cac:TaxSubtotal'); - $x->startElement('cbc:TaxableAmount'); - $x->writeAttribute('currencyID', $currency); - $x->text($invoice->net); - $x->endElement(); - $x->startElement('cbc:TaxAmount'); - $x->writeAttribute('currencyID', $currency); - $x->text($invoice->vat); - $x->endElement(); - $x->startElement('cac:TaxCategory'); - // See https://developer.vertexinc.com/einvoicing/docs/en-16931-tax-categories - $x->writeElement('cbc:ID', $invoice->taxable ? 'S' : 'G'); - $x->writeElement('cbc:Percent', $invoice->taxable ? $invoice->vat_rate*100 : 0); - if (!$invoice->taxable) { - $x->writeElement('cbc:TaxExemptionReasonCode', 'VATEX-EU-G'); - } - $x->startElement('cac:TaxScheme'); - $x->writeElement('cbc:ID', 'VAT'); - $x->endElement(); - $x->endElement(); - $x->endElement(); - $x->endElement(); - // Finances - $x->startElement('cac:LegalMonetaryTotal'); - $x->startElement('cbc:LineExtensionAmount'); - $x->writeAttribute('currencyID', $currency); - $x->text($invoice->net); - $x->endElement(); - $x->startElement('cbc:TaxExclusiveAmount'); - $x->writeAttribute('currencyID', $currency); - $x->text($invoice->net); - $x->endElement(); - $x->startElement('cbc:TaxInclusiveAmount'); - $x->writeAttribute('currencyID', $currency); - $x->text($invoice->gross); - $x->endElement(); - $x->startElement('cbc:ChargeTotalAmount'); - $x->writeAttribute('currencyID', $currency); - $x->text(0); - $x->endElement(); - $x->startElement('cbc:PayableAmount'); - $x->writeAttribute('currencyID', $currency); - $x->text($invoice->gross); - $x->endElement(); - $x->endElement(); - // Positions - foreach($invoice->positions as $key => $position){ - $x->startElement('cac:InvoiceLine'); - $x->writeElement('cbc:ID', $key+1); - $x->startElement('cbc:InvoicedQuantity'); - $x->writeAttribute('unitCode', 'EA'); - $x->text($position->duration); - $x->endElement(); - $x->startElement('cbc:LineExtensionAmount'); - $x->writeAttribute('currencyID', $currency); - $x->text($position->net); - $x->endElement(); - $x->startElement('cac:Item'); - $x->writeElement('cbc:Description', $position->description); - $x->writeElement('cbc:Name', trans_choice('position', 1, locale: $lang)); - $x->startElement('cac:ClassifiedTaxCategory'); - $x->writeElement('cbc:ID', $invoice->taxable ? 'S' : 'G'); - $x->writeElement('cbc:Percent', $invoice->taxable ? $invoice->vat_rate*100 : 0); - $x->startElement('cac:TaxScheme'); - $x->writeElement('cbc:ID', 'VAT'); - $x->endElement(); - $x->endElement(); - $x->endElement(); - $x->startElement('cac:Price'); - $x->startElement('cbc:PriceAmount'); - $x->writeAttribute('currencyID', $currency); - $x->text($invoice->price); - $x->endElement(); - $x->endElement(); - $x->endElement(); - } + $x->startElement('Invoice'); + $x->writeAttribute('xmlns:cac', 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'); + $x->writeAttribute('xmlns:cbc', 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'); + $x->writeAttribute('xmlns:qdt', 'urn:oasis:names:specification:ubl:schema:xsd:QualifiedDataTypes-2'); + $x->writeAttribute('xmlns:udt', 'urn:oasis:names:specification:ubl:schema:xsd:UnqualifiedDataTypes-2'); + $x->writeAttribute('xmlns:ccts', 'urn:un:unece:uncefact:documentation:2'); + $x->writeAttribute('xmlns', 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'); + $x->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + $x->writeAttribute('xsi:schemaLocation', 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2 http://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-Invoice-2.1.xsd'); + // Meta + $x->writeElement('cbc:CustomizationID', 'urn:cen.eu:en16931:2017'); + $x->writeElement('cbc:ID', $invoice->current_number); + $x->writeElement('cbc:IssueDate', Carbon::now()->format('Y-m-d')); + $x->writeElement('cbc:DueDate', Carbon::now()->addWeeks(2)->format('Y-m-d')); + // 380 Rechnung + // 381 Gutschrift + // 384 Rechnungskorrektur + $x->writeElement('cbc:InvoiceTypeCode', 380); + $x->writeElement('cbc:DocumentCurrencyCode', $currency); + // Contractor (me) + $x->startElement('cac:AccountingSupplierParty'); + $x->startElement('cac:Party'); + $x->startElement('cac:PostalAddress'); + $x->writeElement('cbc:StreetName', $settings['street']); + $x->writeElement('cbc:CityName', $settings['city']); + $x->writeElement('cbc:PostalZone', $settings['zip']); + $x->startElement('cac:Country'); + $x->writeElement('cbc:IdentificationCode', 'DE'); // TODO: $settings['country'] + $x->endElement(); + $x->endElement(); + $x->startElement('cac:PartyTaxScheme'); + $x->writeElement('cbc:CompanyID', $settings['vatId']); + $x->startElement('cac:TaxScheme'); + $x->writeElement('cbc:ID', 'VAT'); + $x->endElement(); + $x->endElement(); + $x->startElement('cac:PartyLegalEntity'); + $x->writeElement('cbc:RegistrationName', $settings['name']); + $x->writeElement('cbc:CompanyID', $settings['vatId']); + $x->endElement(); + $x->startElement('cac:Contact'); + $x->writeElement('cbc:ElectronicMail', $settings['email']); + $x->endElement(); + $x->endElement(); + $x->endElement(); + // Client + $x->startElement('cac:AccountingCustomerParty'); + $x->startElement('cac:Party'); + $x->startElement('cac:PostalAddress'); + $x->writeElement('cbc:StreetName', $client?->street); + $x->writeElement('cbc:CityName', $client?->city); + $x->writeElement('cbc:PostalZone', $client?->zip); + $x->startElement('cac:Country'); + $x->writeElement('cbc:IdentificationCode', $client?->country); + $x->endElement(); + $x->endElement(); + $x->startElement('cac:PartyTaxScheme'); + if ($client?->vat_id) { + $x->writeElement('cbc:CompanyID', $client->vat_id); + } + $x->startElement('cac:TaxScheme'); + $x->writeElement('cbc:ID', 'VAT'); + $x->endElement(); + $x->endElement(); + $x->startElement('cac:PartyLegalEntity'); + $x->writeElement('cbc:RegistrationName', $client?->name); + if ($client?->vat_id) { + $x->writeElement('cbc:CompanyID', $client->vat_id); + } + $x->endElement(); + $x->endElement(); + $x->endElement(); + // Payment + $x->startElement('cac:PaymentMeans'); + // 58 SEPA credit transfer + // 59 SEPA direct debit + // 57 Standing agreement + // 30 Credit transfer (non-SEPA) + // 49 Direct debit (non-SEPA) + // 48 Bank card + $x->writeElement('cbc:PaymentMeansCode', 30); + $x->writeElement('cbc:PaymentID', $invoice->current_number); + $x->startElement('cac:PayeeFinancialAccount'); + $x->writeElement('cbc:ID', $settings['iban']); + $x->endElement(); + $x->endElement(); + // Tax + $x->startElement('cac:TaxTotal'); + $x->startElement('cbc:TaxAmount'); + $x->writeAttribute('currencyID', $currency); + $x->text($invoice->vat); + $x->endElement(); + $x->startElement('cac:TaxSubtotal'); + $x->startElement('cbc:TaxableAmount'); + $x->writeAttribute('currencyID', $currency); + $x->text($invoice->net); + $x->endElement(); + $x->startElement('cbc:TaxAmount'); + $x->writeAttribute('currencyID', $currency); + $x->text($invoice->vat); + $x->endElement(); + $x->startElement('cac:TaxCategory'); + // See https://developer.vertexinc.com/einvoicing/docs/en-16931-tax-categories + $x->writeElement('cbc:ID', $invoice->taxable ? 'S' : 'G'); + $x->writeElement('cbc:Percent', $invoice->taxable ? $invoice->vat_rate * 100 : 0); + if (!$invoice->taxable) { + $x->writeElement('cbc:TaxExemptionReasonCode', 'VATEX-EU-G'); + } + $x->startElement('cac:TaxScheme'); + $x->writeElement('cbc:ID', 'VAT'); + $x->endElement(); + $x->endElement(); + $x->endElement(); + $x->endElement(); + // Finances + $x->startElement('cac:LegalMonetaryTotal'); + $x->startElement('cbc:LineExtensionAmount'); + $x->writeAttribute('currencyID', $currency); + $x->text($invoice->net); + $x->endElement(); + $x->startElement('cbc:TaxExclusiveAmount'); + $x->writeAttribute('currencyID', $currency); + $x->text($invoice->net); + $x->endElement(); + $x->startElement('cbc:TaxInclusiveAmount'); + $x->writeAttribute('currencyID', $currency); + $x->text($invoice->gross); + $x->endElement(); + $x->startElement('cbc:ChargeTotalAmount'); + $x->writeAttribute('currencyID', $currency); + $x->text(0); + $x->endElement(); + $x->startElement('cbc:PayableAmount'); + $x->writeAttribute('currencyID', $currency); + $x->text($invoice->gross); + $x->endElement(); + $x->endElement(); + // Positions + foreach ($invoice->positions as $key => $position) { + $x->startElement('cac:InvoiceLine'); + $x->writeElement('cbc:ID', $key + 1); + $x->startElement('cbc:InvoicedQuantity'); + $x->writeAttribute('unitCode', 'EA'); + $x->text($position->duration); + $x->endElement(); + $x->startElement('cbc:LineExtensionAmount'); + $x->writeAttribute('currencyID', $currency); + $x->text($position->net); + $x->endElement(); + $x->startElement('cac:Item'); + $x->writeElement('cbc:Description', $position->description); + $x->writeElement('cbc:Name', trans_choice('position', 1, locale: $lang)); + $x->startElement('cac:ClassifiedTaxCategory'); + $x->writeElement('cbc:ID', $invoice->taxable ? 'S' : 'G'); + $x->writeElement('cbc:Percent', $invoice->taxable ? $invoice->vat_rate * 100 : 0); + $x->startElement('cac:TaxScheme'); + $x->writeElement('cbc:ID', 'VAT'); $x->endElement(); + $x->endElement(); + $x->endElement(); + $x->startElement('cac:Price'); + $x->startElement('cbc:PriceAmount'); + $x->writeAttribute('currencyID', $currency); + $x->text($invoice->price); + $x->endElement(); + $x->endElement(); + $x->endElement(); + } + $x->endElement(); $x->endDocument(); $x->flush(); unset($x); diff --git a/app/Services/PdfTemplate.php b/app/Services/PdfTemplate.php index 458963f..28c96b6 100644 --- a/app/Services/PdfTemplate.php +++ b/app/Services/PdfTemplate.php @@ -55,7 +55,7 @@ public function header(): void DocumentType::QUOTE => $this->getPage() <= 2 ? __("quote", locale: $this->lang) : __("costEstimate", locale: $this->lang), - } + }, ); $this->setFont('FiraSans-ExtraLight', fontSizeInPoint: 26) ->setTextColor(Color::LIGHT->pdfColor()) @@ -181,8 +181,8 @@ protected function putCatalog(): void } $this->writer->putf('/Names <>', $this->attachmentNumber); $array = \array_map( - static fn (PdfAttachment $attachment): string => $attachment->formatNumber(), - $this->attachments + static fn(PdfAttachment $attachment): string => $attachment->formatNumber(), + $this->attachments, ); $this->writer->putf('/AF [%s]', \implode(' ', $array)); } diff --git a/app/Services/ProjectService.php b/app/Services/ProjectService.php index 03da62e..00704b5 100644 --- a/app/Services/ProjectService.php +++ b/app/Services/ProjectService.php @@ -23,7 +23,8 @@ class ProjectService * @param Project $project Record to export * @return string */ - public static function generateQuotePdf(Project $project): string { + public static function generateQuotePdf(Project $project): string + { $conf = Setting::pluck('value', 'field'); $client = $project?->client; $lang = $client?->language->value ?? 'de'; @@ -42,12 +43,12 @@ public static function generateQuotePdf(Project $project): string { 'hours' => Number::format($billedPerProject ? $project->scope ?? 0 : $project->estimated_hours, 1, locale: $lang), 'net' => Number::currency($project->estimated_net, 'EUR', locale: $lang), 'number' => $now->format('Ymd'), - 'price' => Number::currency($billedPerProject ? ($project->scope ? $project->price/$project->scope : 0) : $project->price, 'EUR', locale: $lang), + 'price' => Number::currency($billedPerProject ? ($project->scope ? $project->price / $project->scope : 0) : $project->price, 'EUR', locale: $lang), 'start' => Carbon::parse($project->start_at)->locale($lang)->isoFormat('LL'), 'title' => $project->title, 'validDate' => $now->addWeeks(3)->locale($lang)->isoFormat('LL'), 'vat' => Number::currency($project->estimated_vat, 'EUR', locale: $lang), - 'vatRate' => Number::percentage($conf['vatRate']*100, 2, locale: $lang) . ' ' . __("vat", locale: $lang), + 'vatRate' => Number::percentage($conf['vatRate'] * 100, 2, locale: $lang) . ' ' . __("vat", locale: $lang), ]); $label = collect([ @@ -93,9 +94,9 @@ public static function generateQuotePdf(Project $project): string { ]); // Convert to supported char encoding - $conf = $conf->map(fn ($e) => iconv('UTF-8', 'windows-1252', $e)); - $data = $data->map(fn ($e) => iconv('UTF-8', 'windows-1252', $e)); - $label = $label->map(fn ($e) => iconv('UTF-8', 'windows-1252', $e)); + $conf = $conf->map(fn($e) => iconv('UTF-8', 'windows-1252', $e)); + $data = $data->map(fn($e) => iconv('UTF-8', 'windows-1252', $e)); + $label = $label->map(fn($e) => iconv('UTF-8', 'windows-1252', $e)); // Init document $pdf = new PdfTemplate($lang, DocumentType::QUOTE); @@ -232,7 +233,8 @@ public static function generateQuotePdf(Project $project): string { $rowHeight = 3.5; $totalHeight = array_reduce( $estimates, - fn ($a, $c) => $a + count(explode("\n", $c->description)) + 2, 0 + fn($a, $c) => $a + count(explode("\n", $c->description)) + 2, + 0, ) * $rowHeight + 32; $pdf->addPage(); @@ -286,7 +288,7 @@ public static function generateQuotePdf(Project $project): string { ]); // Convert to supported char encoding - $estData = $estData->map(fn ($e) => iconv('UTF-8', 'windows-1252', $e)); + $estData = $estData->map(fn($e) => iconv('UTF-8', 'windows-1252', $e)); $pdf->setTextColor(Color::DARK->pdfColor()) ->setFont('FiraSans-Regular') diff --git a/bootstrap/app.php b/bootstrap/app.php index 037e17d..e6322c4 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -12,7 +12,7 @@ */ $app = new Illuminate\Foundation\Application( - $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__) + $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__), ); /* @@ -28,17 +28,17 @@ $app->singleton( Illuminate\Contracts\Http\Kernel::class, - App\Http\Kernel::class + App\Http\Kernel::class, ); $app->singleton( Illuminate\Contracts\Console\Kernel::class, - App\Console\Kernel::class + App\Console\Kernel::class, ); $app->singleton( Illuminate\Contracts\Debug\ExceptionHandler::class, - App\Exceptions\Handler::class + App\Exceptions\Handler::class, ); /* diff --git a/composer.json b/composer.json index 1759f35..7c9367d 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { - "name": "laravel/laravel", + "name": "devmount/sales", "type": "project", - "description": "The skeleton application for the Laravel framework.", + "description": "A service sales manager made for freelancers", "keywords": [ "laravel", "framework" @@ -54,7 +54,9 @@ ], "post-create-project-cmd": [ "@php artisan key:generate --ansi" - ] + ], + "lint": "vendor/bin/pint --test --parallel", + "format": "vendor/bin/pint --parallel" }, "extra": { "laravel": { diff --git a/config/app.php b/config/app.php index 4c84256..ca6391f 100644 --- a/config/app.php +++ b/config/app.php @@ -18,17 +18,17 @@ 'name' => env('APP_NAME', 'Laravel'), - /* - |-------------------------------------------------------------------------- - | Application Version - |-------------------------------------------------------------------------- - | - | This value is the version of your application. This value is used when - | the framework needs to place the application's version in a notification - | or any other location as required by the application or its packages. - */ - - 'version' => '2.9.0', + /* + |-------------------------------------------------------------------------- + | Application Version + |-------------------------------------------------------------------------- + | + | This value is the version of your application. This value is used when + | the framework needs to place the application's version in a notification + | or any other location as required by the application or its packages. + */ + + 'version' => '2.9.0', /* |-------------------------------------------------------------------------- diff --git a/config/cache.php b/config/cache.php index d4171e2..5115f18 100644 --- a/config/cache.php +++ b/config/cache.php @@ -106,6 +106,6 @@ | */ - 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), + 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_cache_'), ]; diff --git a/config/database.php b/config/database.php index d21f543..9b6a7c6 100644 --- a/config/database.php +++ b/config/database.php @@ -125,7 +125,7 @@ 'options' => [ '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_'), ], 'default' => [ diff --git a/config/filesystems.php b/config/filesystems.php index 1ab0d03..99861bc 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -40,7 +40,7 @@ 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), - 'url' => env('APP_URL').'/storage', + 'url' => env('APP_URL') . '/storage', 'visibility' => 'public', 'throw' => false, ], diff --git a/config/logging.php b/config/logging.php index c44d276..d000835 100644 --- a/config/logging.php +++ b/config/logging.php @@ -89,7 +89,7 @@ 'handler_with' => [ 'host' => env('PAPERTRAIL_URL'), 'port' => env('PAPERTRAIL_PORT'), - 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), + 'connectionString' => 'tls://' . env('PAPERTRAIL_URL') . ':' . env('PAPERTRAIL_PORT'), ], 'processors' => [PsrLogMessageProcessor::class], ], diff --git a/config/sanctum.php b/config/sanctum.php index 529cfdc..c393e78 100644 --- a/config/sanctum.php +++ b/config/sanctum.php @@ -18,7 +18,7 @@ 'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( '%s%s', 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', - Sanctum::currentApplicationUrlWithPort() + Sanctum::currentApplicationUrlWithPort(), ))), /* diff --git a/config/session.php b/config/session.php index 8fed97c..5fb15f8 100644 --- a/config/session.php +++ b/config/session.php @@ -128,7 +128,7 @@ 'cookie' => env( 'SESSION_COOKIE', - Str::slug(env('APP_NAME', 'laravel'), '_').'_session' + Str::slug(env('APP_NAME', 'laravel'), '_') . '_session', ), /* diff --git a/config/view.php b/config/view.php index 22b8a18..40cbf77 100644 --- a/config/view.php +++ b/config/view.php @@ -30,7 +30,7 @@ 'compiled' => env( 'VIEW_COMPILED_PATH', - realpath(storage_path('framework/views')) + realpath(storage_path('framework/views')), ), ]; diff --git a/database/factories/ExpenseFactory.php b/database/factories/ExpenseFactory.php index ac2a049..180e7be 100644 --- a/database/factories/ExpenseFactory.php +++ b/database/factories/ExpenseFactory.php @@ -43,7 +43,7 @@ public function definition(): array */ public function notTaxable(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'taxable' => false, 'vat_rate' => null, ]); diff --git a/database/factories/InvoiceFactory.php b/database/factories/InvoiceFactory.php index 12cfbf0..f238d19 100644 --- a/database/factories/InvoiceFactory.php +++ b/database/factories/InvoiceFactory.php @@ -52,7 +52,7 @@ public function definition(): array */ public function active(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'invoiced_at' => null, 'paid_at' => null, ]); @@ -63,7 +63,7 @@ public function active(): static */ public function waiting(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'invoiced_at' => fake()->dateTimeBetween('-6 months', 'now')->format('Y-m-d'), 'paid_at' => null, ]); @@ -75,7 +75,7 @@ public function waiting(): static public function finished(): static { $invoicedAt = fake()->dateTimeBetween('-6 months', 'now')->format('Y-m-d'); - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'invoiced_at' => $invoicedAt, 'paid_at' => fake()->dateTimeBetween($invoicedAt, '+7 days')->format('Y-m-d'), ]); @@ -86,7 +86,7 @@ public function finished(): static */ public function taxable(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'taxable' => true, 'vat_rate' => fake()->randomElement([0.07, 0.19]), ]); @@ -97,7 +97,7 @@ public function taxable(): static */ public function notTaxable(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'taxable' => false, 'vat_rate' => null, ]); @@ -108,7 +108,7 @@ public function notTaxable(): static */ public function hourly(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'price' => fake()->randomFloat(2, 50, 150), 'pricing_unit' => PricingUnit::Hour->value, ]); @@ -119,7 +119,7 @@ public function hourly(): static */ public function daily(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'price' => fake()->randomFloat(2, 450, 1250), 'pricing_unit' => PricingUnit::Day->value, ]); @@ -130,7 +130,7 @@ public function daily(): static */ public function project(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'price' => fake()->randomFloat(2, 500, 15000), 'pricing_unit' => PricingUnit::Project->value, ]); diff --git a/database/factories/OfftimeFactory.php b/database/factories/OfftimeFactory.php index a8ef4ba..eaf8de2 100644 --- a/database/factories/OfftimeFactory.php +++ b/database/factories/OfftimeFactory.php @@ -41,7 +41,7 @@ public function definition(): array */ public function singleDay(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'end' => null, ]); } @@ -51,7 +51,7 @@ public function singleDay(): static */ public function vacation(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'category' => OfftimeCategory::Vacation->value, ]); } @@ -61,7 +61,7 @@ public function vacation(): static */ public function sick(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'category' => OfftimeCategory::Sick->value, ]); } diff --git a/database/factories/PositionFactory.php b/database/factories/PositionFactory.php index 2dd5b69..47e652a 100644 --- a/database/factories/PositionFactory.php +++ b/database/factories/PositionFactory.php @@ -44,7 +44,7 @@ public function definition(): array */ public function remote(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'remote' => true, ]); } @@ -54,7 +54,7 @@ public function remote(): static */ public function onSite(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'remote' => false, ]); } diff --git a/database/factories/ProjectFactory.php b/database/factories/ProjectFactory.php index fb5df12..ad8e7ce 100644 --- a/database/factories/ProjectFactory.php +++ b/database/factories/ProjectFactory.php @@ -48,7 +48,7 @@ public function definition(): array */ public function active(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'start_at' => fake()->dateTimeBetween('-3 months', 'now')->format('Y-m-d'), 'due_at' => fake()->dateTimeBetween('now', '+3 months')->format('Y-m-d'), 'aborted' => false, @@ -60,7 +60,7 @@ public function active(): static */ public function upcoming(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'start_at' => fake()->dateTimeBetween('now', '+2 months')->format('Y-m-d'), 'due_at' => fake()->dateTimeBetween('+6 months', '+12 months')->format('Y-m-d'), 'aborted' => false, @@ -72,7 +72,7 @@ public function upcoming(): static */ public function finished(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'start_at' => fake()->dateTimeBetween('-12 months', '-6 months')->format('Y-m-d'), 'due_at' => fake()->dateTimeBetween('-4 months', 'now')->format('Y-m-d'), 'aborted' => false, @@ -84,7 +84,7 @@ public function finished(): static */ public function aborted(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'aborted' => true, ]); } @@ -94,7 +94,7 @@ public function aborted(): static */ public function hourly(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'price' => fake()->randomFloat(2, 50, 150), 'pricing_unit' => PricingUnit::Hour->value, ]); @@ -105,7 +105,7 @@ public function hourly(): static */ public function daily(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'price' => fake()->randomFloat(2, 450, 1250), 'pricing_unit' => PricingUnit::Day->value, ]); @@ -116,7 +116,7 @@ public function daily(): static */ public function project(): static { - return $this->state(fn (array $attributes) => [ + return $this->state(fn(array $attributes) => [ 'price' => fake()->randomFloat(2, 500, 15000), 'pricing_unit' => PricingUnit::Project->value, ]); diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 28e61a9..6d2d641 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -32,7 +32,7 @@ public function definition(): array */ public function unverified(): static { - return $this->state(fn () => [ + return $this->state(fn() => [ 'email_verified_at' => null, ]); } diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php index 444fafb..814b11a 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php b/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php index 81a7229..1bc0eee 100644 --- a/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php +++ b/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2019_08_19_000000_create_failed_jobs_table.php b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php index 249da81..51aaa6f 100644 --- a/database/migrations/2019_08_19_000000_create_failed_jobs_table.php +++ b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php b/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php index e828ad8..82dbff5 100644 --- a/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php +++ b/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2023_08_22_141844_create_clients_table.php b/database/migrations/2023_08_22_141844_create_clients_table.php index eecf8b4..8097abf 100644 --- a/database/migrations/2023_08_22_141844_create_clients_table.php +++ b/database/migrations/2023_08_22_141844_create_clients_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2023_08_23_123436_create_gifts_table.php b/database/migrations/2023_08_23_123436_create_gifts_table.php index 21cd860..61f2ff0 100644 --- a/database/migrations/2023_08_23_123436_create_gifts_table.php +++ b/database/migrations/2023_08_23_123436_create_gifts_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2023_08_24_040548_create_expenses_table.php b/database/migrations/2023_08_24_040548_create_expenses_table.php index 48a44ad..eb06447 100644 --- a/database/migrations/2023_08_24_040548_create_expenses_table.php +++ b/database/migrations/2023_08_24_040548_create_expenses_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2023_08_25_122638_create_projects_table.php b/database/migrations/2023_08_25_122638_create_projects_table.php index c0ada6c..8197ab3 100644 --- a/database/migrations/2023_08_25_122638_create_projects_table.php +++ b/database/migrations/2023_08_25_122638_create_projects_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2023_08_26_192313_create_estimates_table.php b/database/migrations/2023_08_26_192313_create_estimates_table.php index 44963e4..7be7392 100644 --- a/database/migrations/2023_08_26_192313_create_estimates_table.php +++ b/database/migrations/2023_08_26_192313_create_estimates_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2023_08_27_192643_create_invoices_table.php b/database/migrations/2023_08_27_192643_create_invoices_table.php index 570248f..8194bd0 100644 --- a/database/migrations/2023_08_27_192643_create_invoices_table.php +++ b/database/migrations/2023_08_27_192643_create_invoices_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2023_08_28_134626_create_positions_table.php b/database/migrations/2023_08_28_134626_create_positions_table.php index 89a0a7c..870a3f9 100644 --- a/database/migrations/2023_08_28_134626_create_positions_table.php +++ b/database/migrations/2023_08_28_134626_create_positions_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2023_09_01_181022_create_settings_table.php b/database/migrations/2023_09_01_181022_create_settings_table.php index c24120b..ccbb921 100644 --- a/database/migrations/2023_09_01_181022_create_settings_table.php +++ b/database/migrations/2023_09_01_181022_create_settings_table.php @@ -5,8 +5,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2023_09_19_000001_modify_expenses_table.php b/database/migrations/2023_09_19_000001_modify_expenses_table.php index e68b88f..58987e4 100644 --- a/database/migrations/2023_09_19_000001_modify_expenses_table.php +++ b/database/migrations/2023_09_19_000001_modify_expenses_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2024_12_18_215647_add_address_fields_to_clients_table.php b/database/migrations/2024_12_18_215647_add_address_fields_to_clients_table.php index ba36994..5b3d429 100644 --- a/database/migrations/2024_12_18_215647_add_address_fields_to_clients_table.php +++ b/database/migrations/2024_12_18_215647_add_address_fields_to_clients_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2025_05_03_210539_create_offtimes.php b/database/migrations/2025_05_03_210539_create_offtimes.php index 55b8134..6bda6d0 100644 --- a/database/migrations/2025_05_03_210539_create_offtimes.php +++ b/database/migrations/2025_05_03_210539_create_offtimes.php @@ -5,8 +5,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ diff --git a/pint.json b/pint.json new file mode 100644 index 0000000..21f4155 --- /dev/null +++ b/pint.json @@ -0,0 +1,27 @@ +{ + "preset": "per", + "rules": { + "ordered_class_elements": { + "order": [ + "use_trait", + "case", + "constant_public", + "constant_protected", + "constant_private", + "property_public", + "property_protected", + "property_private", + "construct", + "destruct", + "magic", + "method_public", + "method_protected", + "method_private" + ], + "sort_algorithm": "none" + }, + "types_spaces": { + "space": "none" + } + } +} diff --git a/public/index.php b/public/index.php index 1d69f3a..9dd7374 100644 --- a/public/index.php +++ b/public/index.php @@ -16,7 +16,7 @@ | */ -if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) { +if (file_exists($maintenance = __DIR__ . '/../storage/framework/maintenance.php')) { require $maintenance; } @@ -31,7 +31,7 @@ | */ -require __DIR__.'/../vendor/autoload.php'; +require __DIR__ . '/../vendor/autoload.php'; /* |-------------------------------------------------------------------------- @@ -44,12 +44,12 @@ | */ -$app = require_once __DIR__.'/../bootstrap/app.php'; +$app = require_once __DIR__ . '/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( - $request = Request::capture() + $request = Request::capture(), )->send(); $kernel->terminate($request, $response); diff --git a/routes/web.php b/routes/web.php index 83469ef..c6a13f8 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,5 @@ call('save') ->assertHasFormErrors(['password']); } + + protected function setUp(): void + { + parent::setUp(); + + // The "save" action is rate-limited by component+method+IP via the file + // cache driver, which isn't reset between tests like the database is. + Cache::flush(); + } } diff --git a/tests/Feature/InvoiceServiceTest.php b/tests/Feature/InvoiceServiceTest.php index e242b7e..d9839cf 100644 --- a/tests/Feature/InvoiceServiceTest.php +++ b/tests/Feature/InvoiceServiceTest.php @@ -21,84 +21,6 @@ class InvoiceServiceTest extends TestCase private string $logoPath; private string $signaturePath; - protected function setUp(): void - { - parent::setUp(); - - Storage::fake(); - - $this->logoPath = tempnam(sys_get_temp_dir(), 'logo') . '.jpg'; - imagejpeg(imagecreatetruecolor(10, 10), $this->logoPath); - - $this->signaturePath = tempnam(sys_get_temp_dir(), 'signature') . '.png'; - imagepng(imagecreatetruecolor(10, 10), $this->signaturePath); - - $this->seedSettings(); - } - - protected function tearDown(): void - { - @unlink($this->logoPath); - @unlink($this->signaturePath); - - parent::tearDown(); - } - - private function seedSettings(): void - { - $values = [ - 'accountHolder' => 'Account Holder', - 'bank' => 'Test Bank', - 'bic' => 'TESTBIC1', - 'city' => 'Berlin', - 'company' => 'Acme UG', - 'country' => 'Germany', - 'email' => 'contact@acme.test', - 'iban' => 'DE00000000000000000000', - 'logo' => $this->logoPath, - 'name' => 'Acme UG', - 'phone' => '+49123456789', - 'signature' => $this->signaturePath, - 'street' => 'Main Street 1', - 'taxOffice' => 'Finanzamt Berlin', - 'vatId' => 'DE123456789', - 'vatRate' => '0.19', - 'website' => 'https://acme.test', - 'zip' => '12345', - ]; - - foreach ($values as $field => $value) { - Setting::where('field', $field)->update(['value' => $value]); - } - } - - private function makeInvoice(array $attributes = []): Invoice - { - $client = Client::factory()->create([ - 'language' => $attributes['language'] ?? LanguageCode::DE, - 'name' => 'Client GmbH', - ]); - unset($attributes['language']); - $project = Project::factory()->for($client)->hourly()->create(); - - return Invoice::factory()->for($project)->create(array_merge([ - 'taxable' => true, - 'vat_rate' => 0.19, - 'discount' => null, - 'undated' => false, - ], $attributes)); - } - - private function expectedPdfFilename(Invoice $invoice, string $locale = 'de'): string - { - return strtolower("{$invoice->current_number}_" . trans_choice('invoice', 1, [], $locale) . '_Acme UG.pdf'); - } - - private function expectedXmlFilename(Invoice $invoice, string $locale = 'de'): string - { - return strtolower("{$invoice->current_number}_" . trans_choice('invoice', 1, [], $locale) . '_Acme UG.xml'); - } - #[Test] public function it_generates_and_saves_an_invoice_pdf_with_an_xml_attachment(): void { @@ -200,4 +122,82 @@ public function it_generates_an_en16931_xml_invoice_with_the_expected_content(): $this->assertStringContainsString('' . $invoice->project->client->name . '', $xml); $this->assertStringContainsString('' . $invoice->gross . '', $xml); } + + protected function setUp(): void + { + parent::setUp(); + + Storage::fake(); + + $this->logoPath = tempnam(sys_get_temp_dir(), 'logo') . '.jpg'; + imagejpeg(imagecreatetruecolor(10, 10), $this->logoPath); + + $this->signaturePath = tempnam(sys_get_temp_dir(), 'signature') . '.png'; + imagepng(imagecreatetruecolor(10, 10), $this->signaturePath); + + $this->seedSettings(); + } + + protected function tearDown(): void + { + @unlink($this->logoPath); + @unlink($this->signaturePath); + + parent::tearDown(); + } + + private function seedSettings(): void + { + $values = [ + 'accountHolder' => 'Account Holder', + 'bank' => 'Test Bank', + 'bic' => 'TESTBIC1', + 'city' => 'Berlin', + 'company' => 'Acme UG', + 'country' => 'Germany', + 'email' => 'contact@acme.test', + 'iban' => 'DE00000000000000000000', + 'logo' => $this->logoPath, + 'name' => 'Acme UG', + 'phone' => '+49123456789', + 'signature' => $this->signaturePath, + 'street' => 'Main Street 1', + 'taxOffice' => 'Finanzamt Berlin', + 'vatId' => 'DE123456789', + 'vatRate' => '0.19', + 'website' => 'https://acme.test', + 'zip' => '12345', + ]; + + foreach ($values as $field => $value) { + Setting::where('field', $field)->update(['value' => $value]); + } + } + + private function makeInvoice(array $attributes = []): Invoice + { + $client = Client::factory()->create([ + 'language' => $attributes['language'] ?? LanguageCode::DE, + 'name' => 'Client GmbH', + ]); + unset($attributes['language']); + $project = Project::factory()->for($client)->hourly()->create(); + + return Invoice::factory()->for($project)->create(array_merge([ + 'taxable' => true, + 'vat_rate' => 0.19, + 'discount' => null, + 'undated' => false, + ], $attributes)); + } + + private function expectedPdfFilename(Invoice $invoice, string $locale = 'de'): string + { + return strtolower("{$invoice->current_number}_" . trans_choice('invoice', 1, [], $locale) . '_Acme UG.pdf'); + } + + private function expectedXmlFilename(Invoice $invoice, string $locale = 'de'): string + { + return strtolower("{$invoice->current_number}_" . trans_choice('invoice', 1, [], $locale) . '_Acme UG.xml'); + } } diff --git a/tests/Feature/OfftimeChartTest.php b/tests/Feature/OfftimeChartTest.php index 4f6d977..8320c16 100644 --- a/tests/Feature/OfftimeChartTest.php +++ b/tests/Feature/OfftimeChartTest.php @@ -63,7 +63,7 @@ public function it_assembles_offtime_and_working_days_per_year(): void $this->assertSame(0, $unplanned['data'][$yearIndex]); $this->assertSame( $weekend['data'][$yearIndex] + $planned['data'][$yearIndex] + $unplanned['data'][$yearIndex], - $total['data'][$yearIndex] + $total['data'][$yearIndex], ); $this->assertSame(1, $worked['data'][$yearIndex]); } diff --git a/tests/Feature/ProjectServiceTest.php b/tests/Feature/ProjectServiceTest.php index ea07ffa..5ea5222 100644 --- a/tests/Feature/ProjectServiceTest.php +++ b/tests/Feature/ProjectServiceTest.php @@ -20,62 +20,6 @@ class ProjectServiceTest extends TestCase private string $logoPath; private string $signaturePath; - protected function setUp(): void - { - parent::setUp(); - - Storage::fake(); - - $this->logoPath = tempnam(sys_get_temp_dir(), 'logo') . '.jpg'; - imagejpeg(imagecreatetruecolor(10, 10), $this->logoPath); - - $this->signaturePath = tempnam(sys_get_temp_dir(), 'signature') . '.png'; - imagepng(imagecreatetruecolor(10, 10), $this->signaturePath); - - $this->seedSettings(); - } - - protected function tearDown(): void - { - @unlink($this->logoPath); - @unlink($this->signaturePath); - - parent::tearDown(); - } - - private function seedSettings(): void - { - $values = [ - 'accountHolder' => 'Account Holder', - 'bank' => 'Test Bank', - 'bic' => 'TESTBIC1', - 'city' => 'Berlin', - 'company' => 'Acme UG', - 'country' => 'Germany', - 'email' => 'contact@acme.test', - 'iban' => 'DE00000000000000000000', - 'logo' => $this->logoPath, - 'name' => 'Acme UG', - 'phone' => '+49123456789', - 'signature' => $this->signaturePath, - 'street' => 'Main Street 1', - 'taxOffice' => 'Finanzamt Berlin', - 'vatId' => 'DE123456789', - 'vatRate' => '0.19', - 'website' => 'https://acme.test', - 'zip' => '12345', - ]; - - foreach ($values as $field => $value) { - Setting::where('field', $field)->update(['value' => $value]); - } - } - - private function expectedFilename(): string - { - return strtolower(now()->format('Ymd') . '_' . __('quote', locale: 'de') . '_Acme UG.pdf'); - } - #[Test] public function it_generates_and_saves_a_quote_pdf_for_an_hourly_project(): void { @@ -133,4 +77,60 @@ public function it_generates_a_quote_pdf_with_multiple_estimates(): void Storage::assertExists($filename); $this->assertStringStartsWith('%PDF-', Storage::get($filename)); } + + protected function setUp(): void + { + parent::setUp(); + + Storage::fake(); + + $this->logoPath = tempnam(sys_get_temp_dir(), 'logo') . '.jpg'; + imagejpeg(imagecreatetruecolor(10, 10), $this->logoPath); + + $this->signaturePath = tempnam(sys_get_temp_dir(), 'signature') . '.png'; + imagepng(imagecreatetruecolor(10, 10), $this->signaturePath); + + $this->seedSettings(); + } + + protected function tearDown(): void + { + @unlink($this->logoPath); + @unlink($this->signaturePath); + + parent::tearDown(); + } + + private function seedSettings(): void + { + $values = [ + 'accountHolder' => 'Account Holder', + 'bank' => 'Test Bank', + 'bic' => 'TESTBIC1', + 'city' => 'Berlin', + 'company' => 'Acme UG', + 'country' => 'Germany', + 'email' => 'contact@acme.test', + 'iban' => 'DE00000000000000000000', + 'logo' => $this->logoPath, + 'name' => 'Acme UG', + 'phone' => '+49123456789', + 'signature' => $this->signaturePath, + 'street' => 'Main Street 1', + 'taxOffice' => 'Finanzamt Berlin', + 'vatId' => 'DE123456789', + 'vatRate' => '0.19', + 'website' => 'https://acme.test', + 'zip' => '12345', + ]; + + foreach ($values as $field => $value) { + Setting::where('field', $field)->update(['value' => $value]); + } + } + + private function expectedFilename(): string + { + return strtolower(now()->format('Ymd') . '_' . __('quote', locale: 'de') . '_Acme UG.pdf'); + } }