From 04afcbee5e17a952aaf990e1b4bc815680809edf Mon Sep 17 00:00:00 2001 From: Oneami <34282948+Oneami@users.noreply.github.com> Date: Wed, 3 Jul 2024 22:10:29 +0300 Subject: [PATCH] New admin: clients (#251) * Update PaymentService.php * Fix BelpostLabelService * Format Oneami code * Format Oneami code * 416. Index page * Format Oneami code * #430 Internal movement: contact person * Format Oneami code * Update PaymentService.php * Update InstallmentOrderService.php * Update InstallmentOrderService.php * New admin: clients * Format Oneami code * Fix InstallmentOrderService * User blacklist * Format Oneami code * Fix fb8e8baf --------- Co-authored-by: Oneami --- src/app/Admin/Controllers/OrderController.php | 5 +- .../Components/Forms/RelationManager.php | 69 ++++++ .../Filament/Resources/User/UserResource.php | 218 ++++++++++++++++++ .../User/UserResource/Pages/CreateUser.php | 11 + .../User/UserResource/Pages/EditUser.php | 19 ++ .../User/UserResource/Pages/ListUsers.php | 19 ++ .../BlacklistRelationManager.php | 55 +++++ .../PaymentsRelationManager.php | 58 +++++ src/app/Models/User/User.php | 32 +++ src/app/Models/User/UserBlacklist.php | 36 +++ src/app/Providers/AdminPanelProvider.php | 7 + .../Order/InstallmentOrderService.php | 10 +- src/app/Services/Payment/PaymentService.php | 2 +- ...29_212014_create_user_blacklists_table.php | 32 +++ .../views/admin/order/order-client.blade.php | 3 + .../filament/forms/relation-manager.blade.php | 23 ++ 16 files changed, 593 insertions(+), 6 deletions(-) create mode 100644 src/app/Filament/Components/Forms/RelationManager.php create mode 100644 src/app/Filament/Resources/User/UserResource.php create mode 100644 src/app/Filament/Resources/User/UserResource/Pages/CreateUser.php create mode 100644 src/app/Filament/Resources/User/UserResource/Pages/EditUser.php create mode 100644 src/app/Filament/Resources/User/UserResource/Pages/ListUsers.php create mode 100644 src/app/Filament/Resources/User/UserResource/RelationManagers/BlacklistRelationManager.php create mode 100644 src/app/Filament/Resources/User/UserResource/RelationManagers/PaymentsRelationManager.php create mode 100644 src/app/Models/User/UserBlacklist.php create mode 100644 src/database/migrations/2024_06_29_212014_create_user_blacklists_table.php create mode 100644 src/resources/views/filament/forms/relation-manager.blade.php diff --git a/src/app/Admin/Controllers/OrderController.php b/src/app/Admin/Controllers/OrderController.php index f3eb67b9..1667354b 100644 --- a/src/app/Admin/Controllers/OrderController.php +++ b/src/app/Admin/Controllers/OrderController.php @@ -202,7 +202,10 @@ protected function form(?int $id = null) $form = new Form(new Order()); $order = $id ? Order::query()->where('id', $id)->with([ 'country', - 'user' => fn ($query) => $query->with(['lastAddress' => fn ($q) => $q->with('country')]), + 'user' => fn ($query) => $query->with([ + 'blacklist', + 'lastAddress.country', + ]), ])->first() : null; $administratorService = app(AdministratorService::class); diff --git a/src/app/Filament/Components/Forms/RelationManager.php b/src/app/Filament/Components/Forms/RelationManager.php new file mode 100644 index 00000000..77c4baf4 --- /dev/null +++ b/src/app/Filament/Components/Forms/RelationManager.php @@ -0,0 +1,69 @@ +relationManager = $relationManager; + + return $this; + } + + /** + * Get the relation manager value by evaluating the relationManager property. + * + * @return string The evaluated relation manager value. + */ + public function getRelationManager(): string + { + return $this->evaluate($this->relationManager); + } + + /** + * Sets the lazy flag for the relation manager. + * + * @param bool|Closure $condition The condition to determine if the relation manager should be lazy. Defaults to true. + * @return static The instance of the relation manager for method chaining. + */ + public function lazy(bool|Closure $condition = true): static + { + $this->isLazy = $condition; + + return $this; + } + + /** + * Determine if the relation manager is lazy. + */ + public function isLazy(): bool + { + return (bool)$this->evaluate($this->isLazy); + } +} diff --git a/src/app/Filament/Resources/User/UserResource.php b/src/app/Filament/Resources/User/UserResource.php new file mode 100644 index 00000000..d22c2bac --- /dev/null +++ b/src/app/Filament/Resources/User/UserResource.php @@ -0,0 +1,218 @@ +schema([ + Tabs::make('Tabs')->tabs([ + Tab::make('Основная информация')->schema([ + TextInput::make('first_name') + ->label('Имя') + ->required() + ->maxLength(255), + TextInput::make('last_name') + ->label('Фамилия') + ->required() + ->maxLength(255), + TextInput::make('patronymic_name') + ->label('Отчество') + ->maxLength(255), + TextInput::make('email') + ->label('E-mail') + ->maxLength(255), + TextInput::make('phone') + ->label('Телефон') + ->tel() + ->maxLength(255), + DatePicker::make('birth_date') + ->label('Дата рождения'), + Select::make('group_id') + ->options(Group::all()->pluck('name', 'id')) + ->label('Группа'), + Section::make('Адреса')->schema([ + Repeater::make('addresses') + ->label('') + ->relationship() + ->schema([ + TextInput::make('zip') + ->label('Почтовый индекс'), + TextInput::make('region') + ->label('Область/край'), + TextInput::make('city') + ->label('Город'), + TextInput::make('district') + ->label('Район'), + TextInput::make('street') + ->label('Улица'), + TextInput::make('house') + ->label('Дом'), + TextInput::make('corpus') + ->label('Корпус'), + TextInput::make('room') + ->label('Квартира'), + TextInput::make('address') + ->label('Адрес'), + Toggle::make('approve') + ->label('Подтверждение о проверке'), + ]) + ->columns(3), + ]), + + Section::make('Отзывы')->schema([ + Repeater::make('reviews') + ->label('') + ->relationship() + ->schema([ + Textarea::make('text') + ->label('Отзыв') + ->rows(2) + ->readOnly(), + ]) + ->addable(false) + ->deletable(false) + ->columns(1), + ]), + + ]), + Tab::make('Паспортные данные')->schema([ + Fieldset::make('passport') + ->relationship('passport') + ->schema([ + TextInput::make('passport_number') + ->label('Номер паспорта') + ->maxLength(255), + TextInput::make('series') + ->label('Серия паспорта') + ->maxLength(255), + TextInput::make('issued_by') + ->label('Кем выдан') + ->maxLength(255), + TextInput::make('issued_date') + ->label('Когда выдан') + ->maxLength(255), + TextInput::make('personal_number') + ->label('Личный номер') + ->maxLength(255), + TextInput::make('registration_address') + ->label('Адрес прописки') + ->maxLength(255), + ]), + ]), + Tab::make('Платежи')->schema([ + RelationManager::make()->manager(PaymentsRelationManager::class)->lazy(true), + ]) + ->columns(1), + Tab::make('Черный список (Лог)')->schema([ + RelationManager::make()->manager(BlacklistRelationManager::class)->lazy(true), + ])->columns(1), + ])->columns(2)->persistTabInQueryString(), + ])->columns(1); + } + + public static function table(Table $table): Table + { + $table->modifyQueryUsing( + fn (Builder $query) => $query->with(['orders.data'])->orderBy('id', 'desc') + ); + + return $table + ->columns([ + Tables\Columns\TextColumn::make('first_name') + ->label('Имя') + ->searchable(), + Tables\Columns\TextColumn::make('last_name') + ->label('Фамилия') + ->searchable(), + Tables\Columns\TextColumn::make('patronymic_name') + ->label('Отчество'), + Tables\Columns\TextColumn::make('email') + ->label('E-mail'), + Tables\Columns\TextColumn::make('phone') + ->label('Телефон'), + Tables\Columns\TextColumn::make('orders') + ->label('Сумма покупок') + ->getStateUsing(function (User $user) { + return $user->completedOrdersCost() . ' руб.'; + }), + Tables\Columns\TextColumn::make('group.name') + ->label('Группа'), + Tables\Columns\TextColumn::make('reviews_count') + ->label('Кол-во отзывов') + ->counts('reviews'), + Tables\Columns\TextColumn::make('lastAddress.address') + ->label('Адрес'), + Tables\Columns\TextColumn::make('created_at') + ->label('Дата регистрации') + ->dateTime('d.m.Y H:i:s'), + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]) + ->filters([ + QueryBuilder::make() + ->constraints([ + TextConstraint::make('first_name')->label('Имя'), + TextConstraint::make('last_name')->label('Фамилия'), + TextConstraint::make('patronymic_name')->label('Отчество'), + TextConstraint::make('phone')->label('Телефон'), + TextConstraint::make('email')->label('E-mail'), + SelectConstraint::make('group.id') + ->options(Group::query()->pluck('name', 'id')) + ->multiple(), + TextConstraint::make('addresses.city')->label('Город'), + TextConstraint::make('addresses.address')->label('Адрес'), + ]), + ], layout: FiltersLayout::AboveContentCollapsible) + ->deferFilters() + ->defaultPaginationPageOption(50); + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListUsers::route('/'), + 'create' => Pages\CreateUser::route('/create'), + 'edit' => Pages\EditUser::route('/{record}/edit'), + ]; + } +} diff --git a/src/app/Filament/Resources/User/UserResource/Pages/CreateUser.php b/src/app/Filament/Resources/User/UserResource/Pages/CreateUser.php new file mode 100644 index 00000000..1d607af3 --- /dev/null +++ b/src/app/Filament/Resources/User/UserResource/Pages/CreateUser.php @@ -0,0 +1,11 @@ +schema([ + Textarea::make('comment') + ->label('Комментарий') + ->rows(2), + ])->columns(1); + } + + public function table(Table $table): Table + { + $table->modifyQueryUsing( + fn (Builder $query) => $query + ->withTrashed() + ->orderBy('id', 'desc') + ); + + return $table + ->columns([ + Tables\Columns\TextColumn::make('comment') + ->label('Комментарий'), + Tables\Columns\TextColumn::make('created_at') + ->label('Дата добавления') + ->dateTime('d.m.Y H:i:s'), + Tables\Columns\TextColumn::make('deleted_at') + ->label('Дата удаления') + ->dateTime('d.m.Y H:i:s'), + ])->headerActions([ + Tables\Actions\CreateAction::make() + ->label('Добавить в черный список'), + ]) + ->actions([ + Tables\Actions\DeleteAction::make() + ->label('Удалить из черного списка'), + ]); + } +} diff --git a/src/app/Filament/Resources/User/UserResource/RelationManagers/PaymentsRelationManager.php b/src/app/Filament/Resources/User/UserResource/RelationManagers/PaymentsRelationManager.php new file mode 100644 index 00000000..ba856da5 --- /dev/null +++ b/src/app/Filament/Resources/User/UserResource/RelationManagers/PaymentsRelationManager.php @@ -0,0 +1,58 @@ +modifyQueryUsing( + fn (Builder $query) => $query->orderBy('id', 'desc') + ); + + return $table + ->columns([ + Tables\Columns\TextColumn::make('created_at') + ->label('Дата/время создания') + ->dateTime('d.m.Y H:i:s'), + Tables\Columns\TextColumn::make('order_id') + ->label('№ заказа'), + Tables\Columns\TextColumn::make('last_status_enum_id') + ->label('Статус') + ->getStateUsing(function (OnlinePayment $payment) { + return $payment->last_status_enum_id?->name(); + }), + Tables\Columns\TextColumn::make('admin.name') + ->label('Менеджер'), + Tables\Columns\TextColumn::make('method_enum_id') + ->label('Способ оплаты') + ->getStateUsing(function (OnlinePayment $payment) { + return $payment->method_enum_id?->name(); + }), + Tables\Columns\TextColumn::make('amount') + ->label('Сумма платежа'), + Tables\Columns\TextColumn::make('paid_amount') + ->label('Сумма оплаченная клиентом'), + Tables\Columns\TextColumn::make('currency_code') + ->label('Код валюты'), + Tables\Columns\TextColumn::make('expires_at') + ->label('Срок действия платежа') + ->dateTime('d.m.Y H:i:s'), + Tables\Columns\TextColumn::make('link') + ->label('Ссылка на оплату') + ->getStateUsing(function (OnlinePayment $payment) { + return 'Ссылка на станицу оплаты'; + })->html(), + ]); + } +} diff --git a/src/app/Models/User/User.php b/src/app/Models/User/User.php index 448361d0..b974db71 100644 --- a/src/app/Models/User/User.php +++ b/src/app/Models/User/User.php @@ -7,11 +7,13 @@ use App\Models\Feedback; use App\Models\Logs\SmsLog; use App\Models\Orders\Order; +use App\Models\Payments\OnlinePayment; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -242,6 +244,36 @@ public function mailings(): HasMany return $this->hasMany(SmsLog::class); } + /** + * Retrieve the blacklist associated with the user. + * + * @return \Illuminate\Database\Eloquent\Relations\HasOne The relationship between the user and the blacklist. + */ + public function blacklist(): HasOne + { + return $this->blacklistLogs()->one(); + } + + /** + * Retrieve the blacklistLogs associated with the user. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany The relationship between the user and the blacklistLogs. + */ + public function blacklistLogs(): HasMany + { + return $this->hasMany(UserBlacklist::class); + } + + /** + * Define a relationship with the user's online payments. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany The relationship between the user and their online payments. + */ + public function payments(): HasManyThrough + { + return $this->hasManyThrough(OnlinePayment::class, Order::class); + } + /** * Get the user promocodes associated with the user. */ diff --git a/src/app/Models/User/UserBlacklist.php b/src/app/Models/User/UserBlacklist.php new file mode 100644 index 00000000..b0d982d7 --- /dev/null +++ b/src/app/Models/User/UserBlacklist.php @@ -0,0 +1,36 @@ +belongsTo(User::class); + } +} diff --git a/src/app/Providers/AdminPanelProvider.php b/src/app/Providers/AdminPanelProvider.php index 30b65e87..ae4d4d95 100644 --- a/src/app/Providers/AdminPanelProvider.php +++ b/src/app/Providers/AdminPanelProvider.php @@ -64,10 +64,17 @@ public function panel(Panel $panel): Panel 'promo' => NavigationGroup::make() ->label('Промо') ->icon('heroicon-o-fire'), + 'user' => NavigationGroup::make() + ->label('Клиенты') + ->icon('heroicon-o-user-group'), 'old-admin-panel' => NavigationGroup::make() ->label('Старая админка') ->icon('heroicon-o-arrow-uturn-left') ->collapsed(), + 'automation' => NavigationGroup::make() + ->label('Автоматизация') + ->icon('heroicon-o-cog-8-tooth') + ->collapsed(), 'management' => NavigationGroup::make() ->label('Управление') ->icon('heroicon-o-cog-6-tooth'), diff --git a/src/app/Services/Order/InstallmentOrderService.php b/src/app/Services/Order/InstallmentOrderService.php index d3456b1f..0aa3cf64 100644 --- a/src/app/Services/Order/InstallmentOrderService.php +++ b/src/app/Services/Order/InstallmentOrderService.php @@ -38,15 +38,17 @@ public function createInstallmentForm(Order $order) $onlinePaymentsSum = $order->getAmountPaidOrders(); $uniqItemsCount = $order->getUniqItemsCount(); - foreach ($order->items as $itemKey => $item) { + $sheetCount = 0; + foreach ($order->items as $item) { if (!$item->installment?->num_payments) { continue; } - if ($itemKey > 0) { + if ($sheetCount > 0) { $spreadsheet->addSheet($firstSheet); - $spreadsheet->setActiveSheetIndex($itemKey); + $spreadsheet->setActiveSheetIndex($sheetCount); + $sheetCount++; } - $sheet = $spreadsheet->getActiveSheet()->setTitle('№' . $itemKey + 1); + $sheet = $spreadsheet->getActiveSheet()->setTitle('№' . $sheetCount + 1); $sheet->setCellValue('AD1', $item->installment->contract_number ?? null); diff --git a/src/app/Services/Payment/PaymentService.php b/src/app/Services/Payment/PaymentService.php index 74c00234..7d94e6c5 100644 --- a/src/app/Services/Payment/PaymentService.php +++ b/src/app/Services/Payment/PaymentService.php @@ -156,7 +156,7 @@ public function autoSetOrderStatus(OnlinePayment $onlinePayment): void $firstPaymentsSum = 0; foreach ($order->data as $orderItem) { $firstPaymentSum = $isInstallment ? ($orderItem->current_price - ($orderItem->installment->monthly_fee * ($orderItem->installment->num_payments - 1))) : 0; - if ($orderItem->current_price == $itemCodSum || ($isInstallment && $firstPaymentSum == $itemCodSum)) { + if (ceil($orderItem->current_price) == ceil($itemCodSum) || ($isInstallment && ceil($firstPaymentSum) == ceil($itemCodSum))) { $partialBuybackItemsCount++; } $firstPaymentsSum += $firstPaymentSum; diff --git a/src/database/migrations/2024_06_29_212014_create_user_blacklists_table.php b/src/database/migrations/2024_06_29_212014_create_user_blacklists_table.php new file mode 100644 index 00000000..84635695 --- /dev/null +++ b/src/database/migrations/2024_06_29_212014_create_user_blacklists_table.php @@ -0,0 +1,32 @@ +id(); + + $table->foreignId('user_id')->comment('ID пользователя')->constrained('users')->cascadeOnUpdate()->cascadeOnDelete(); + $table->text('comment')->comment('Комментарий')->nullable(); + + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_blacklists'); + } +}; diff --git a/src/resources/views/admin/order/order-client.blade.php b/src/resources/views/admin/order/order-client.blade.php index a6e58926..f221a908 100644 --- a/src/resources/views/admin/order/order-client.blade.php +++ b/src/resources/views/admin/order/order-client.blade.php @@ -3,6 +3,9 @@
@if (isset($order->user)) + @if ($order->user->blacklist) +

В черном списке: {{ $order->user->blacklist->comment }}

+ @endif ФИО: {{ $order->user?->last_name }} diff --git a/src/resources/views/filament/forms/relation-manager.blade.php b/src/resources/views/filament/forms/relation-manager.blade.php new file mode 100644 index 00000000..ca17e111 --- /dev/null +++ b/src/resources/views/filament/forms/relation-manager.blade.php @@ -0,0 +1,23 @@ +@php + $manager = $getRelationManager(); + $isLazy = $isLazy(); + + $normalizeRelationManagerClass = function ( + string|Filament\Resources\RelationManagers\RelationManagerConfiguration $manager, + ): string { + if ($manager instanceof \Filament\Resources\RelationManagers\RelationManagerConfiguration) { + return $manager->relationManager; + } + + return $manager; + }; + + $normalizedManagerClass = $normalizeRelationManagerClass($manager); + + $managerLivewireProperties = ['lazy' => $isLazy, 'ownerRecord' => $this->getRecord(), 'pageClass' => $this::class]; +@endphp + + +
+ @livewire($normalizedManagerClass, [...$managerLivewireProperties, ...$manager instanceof \Filament\Resources\RelationManagers\RelationManagerConfiguration ? $manager->properties : []], key($normalizedManagerClass)) +