Compare commits

..

94 Commits

Author SHA1 Message Date
Mekan1206
a5bc322170 Enhance MigrationController and ShieldSeeder for improved data migration and role management
- Added new migrators: ModelHasRolesMigrator and OnlinePaymentsMigrator to MigrationController.
- Introduced a test method in MigrationController to facilitate online payments migration.
- Updated ShieldSeeder to include role IDs for better role management and added new roles with specific IDs.
2025-12-21 20:58:10 +05:00
Mekan1206
749208ac97 Add new migrators to MigrationController for enhanced data handling
- Included CardOrdersMigrator, CardPinOrdersMigrator, and VisaMasterSettingsMigrator to the MigrationController.
- Updated the index method to incorporate new migrators for improved migration processes.
2025-12-21 20:00:14 +05:00
Mekan1206
1870583441 Add Laravel Sanctum for API authentication and update routes
- Included `laravel/sanctum` in `composer.json` for lightweight API authentication.
- Updated `User` model to use `HasApiTokens` trait for token management.
- Configured API routing in `bootstrap/app.php`.
- Modified `DatabaseSeeder` to include `ShieldSeeder` and adjusted `FillJsonData` seeder method.
- Changed JSON data path in `ProvincesMigrator` for testing purposes.
- Updated web routes to utilize `MigrationController` for better organization.
2025-12-21 19:50:38 +05:00
Mekan1206
76c05ebe7c Refactor ActivityLogModule and clean up seeders
- Adjusted indentation in ActivityLogModule methods for consistency.
- Simplified ActivityLogRepository class by removing unnecessary lines.
- Updated FillJsonData seeder to streamline the run method.
- Modified various Migrator classes to change JSON data paths for testing and added sequence reset statements for better database integrity.
2025-12-21 19:16:26 +05:00
Mekan1206
515731003c Remove activity log migration files and update FillJsonData seeder to include ActionEventsMigrator. Clean up LoanOrdersMigrator comments for clarity. 2025-12-21 18:57:21 +05:00
Mekan1206
a9c7ec6b80 Remove pxlrbt/filament-activity-log dependency from composer.json and update composer.lock. Enhance LoginController validation by adding a rule to exclude a specific username. Minor adjustments in migration files for activity log to improve code organization. 2025-12-21 17:51:38 +05:00
Mekan1206
4038d47582 add composer require spatie/laravel-activitylog 2025-12-21 06:42:41 +05:00
Mekan1206
326987d6de Add phone or username placeholder in login view and update Turkish translations
- Updated the login view to include a placeholder for phone or username input.
- Added a new Turkish translation for the phone or username placeholder to enhance user guidance.
2025-12-21 06:20:41 +05:00
Mekan1206
f433fc3e41 Remove unused Blade components and templates from the Filament package to streamline the codebase. This includes deleting various components related to forms, notifications, pagination, and widgets, enhancing maintainability and reducing clutter. 2025-12-21 06:15:33 +05:00
Mekan1206
5361c00e7b images better 2025-12-21 05:31:08 +05:00
Mekan1206
5e37bd1a76 Enhance ListRecords pages with tab functionality and default sorting
- Added getTabs method to ListCardPinOrders, ListLoanOrders, and ListLoanOrderMobiles pages to display order statuses for system users.
- Implemented default sorting by 'created_at' in LoanOrdersTable and LoanOrderMobilesTable for improved data organization.
- Updated ManageCards page to ensure proper type declaration for getTitle method.
2025-12-21 05:21:53 +05:00
Mekan1206
5a53b90272 Add default sorting to CardPinOrdersTable by creation date
- Updated CardPinOrdersTable to default sort by 'created_at' in descending order for improved data visibility.
2025-12-21 05:11:21 +05:00
Mekan1206
59548a486f Enhance CardOrder model and SMS functionality
- Added unique ID generation and SMS notification upon CardOrder creation.
- Updated CardOrdersTable to default sort by creation date.
- Implemented logging for SMS sending in SmsRepository for better debugging.
2025-12-21 05:05:28 +05:00
Mekan1206
c0bfe974ad Add avatar functionality to User model
- Implemented HasAvatar contract in User model to support avatar management.
- Added getFilamentAvatarUrl method to return the default avatar URL.
2025-12-21 04:20:45 +05:00
Mekan1206
895c5f6226 Update ManageCards page and localization for card creation
- Added a label for the 'Add card' action in the ManageCards page.
- Updated Turkish translations for card creation actions and notifications to improve clarity and consistency.
2025-12-21 04:13:47 +05:00
Mekan1206
88d84ac457 Add title method to ManageCards page for improved user experience 2025-12-21 04:01:05 +05:00
Mekan1206
94ad59ce24 Enhance user role management and update Filament panel middleware
- Added new user roles: operator, operator_card, operator_loan, client, and currency_maintainer in ShieldSeeder.
- Included EnsureUserHasRole middleware in the Filament panel for improved access control.
2025-12-21 03:49:55 +05:00
Mekan1206
b1630ea623 Add policy mappings for VisaMasterPaymentOrder and OnlinePayment models 2025-12-21 03:44:14 +05:00
Mekan1206
6a700fbd4b Enhance user role checks and update card order tabs visibility
- Updated the isSystemUser method to include currency maintainers in role checks.
- Modified getTabs method in ListCardOrders to return an empty array for non-system users.
- Added a dehydrate state function for Turkmen phone numbers in UserForm schema.
2025-12-21 03:38:52 +05:00
Mekan1206
8e548126b2 Refactor VisaMasterPaymentOrdersMigrator to enhance data migration process
- Updated the migration logic to insert records into the 'visa_master_payment_orders' table.
- Improved handling of sender and receiver data by decoding JSON fields and providing fallback values.
- Added a test route in web.php to trigger the migration for debugging purposes.
2025-12-21 03:18:50 +05:00
Mekan1206
2ec9b28a60 Refactor VisaMasterPaymentOrder components and enhance seeding process
- Updated record title attribute in VisaMasterPaymentOrderResource to 'unique_id'.
- Improved schema layout in VisaMasterPaymentOrderForm and VisaMasterPaymentOrderInfolist for better user experience.
- Added 'unique_id' field to VisaMasterPaymentOrderInfolist with styling adjustments.
- Implemented BelongsToBranch interface in VisaMasterPaymentOrder model and added a boot method for loan order creation.
- Expanded FillJsonData seeder to include migration for VisaMasterPaymentOrders.
2025-12-21 02:58:15 +05:00
Mekan1206
4fc242bc7d Update dependencies and enhance components
- Added "halaxa/json-machine" dependency to composer.json.
- Updated content hash in composer.lock to reflect changes.
- Improved SpatieMediaLibraryFileEntry component documentation.
- Expanded FillJsonData seeder to include new migrators for card types, orders, and pin orders.
- Updated Tailwind CSS version in app.css.
- Refactored various JavaScript components for better performance and readability.
- Added a test route in web.php for debugging purpose
2025-12-21 02:41:27 +05:00
Mekan1206
6df3a27383 ignore database/data folder 2025-12-20 02:38:06 +05:00
Mekan1206
7111b1db09 Enhance SpatieMediaLibraryFileEntry component and update PHPStan configuration
- Added a new method to retrieve media for the entry in SpatieMediaLibraryFileEntry component.
- Updated PHPStan configuration to exclude the SpatieMediaLibraryFileEntry.php file from analysis.
2025-12-20 02:34:19 +05:00
Mekan1206
c4ee279d91 Refactor VisaMasterPaymentOrderInfolist and SpatieMediaLibraryFileEntry components
- Cleaned up imports and improved code formatting for better readability.
- Adjusted layout in VisaMasterPaymentOrderInfolist schema for consistent column spans.
- Enhanced SpatieMediaLibraryFileEntry component by streamlining property definitions and ensuring consistent formatting.
2025-12-20 02:30:36 +05:00
Mekan1206
14c4ea5791 Update subNavigationPosition in CardsCluster to Top 2025-12-20 02:28:48 +05:00
Mekan1206
01a0aa8ca1 dont ignore public/build 2025-12-20 00:56:53 +05:00
Mekan1206
97fea41b62 WIP 2025-12-20 00:40:22 +05:00
Mekan1206
5568d32a31 Update project name and enable seeders in DatabaseSeeder
- Changed project name in package-lock.json from 'backend_tbbank' to 'online'.
- Enabled UsersTableSeeder and ShieldSeeder in DatabaseSeeder for data seeding.
2025-12-20 00:29:34 +05:00
4e78ff77cc wip 2025-12-19 17:18:36 +05:00
4c0917e6ba Refactor media display logic in SpatieMediaLibraryFileEntry component
- Simplified media handling by removing the check for images only.
- Updated grid layout for media items to use a consistent column size.
- Enhanced modal functionality to open specific image previews based on item ID.
- Improved code readability by renaming variables and streamlining the foreach loop.
2025-12-09 01:40:07 +05:00
c56e3383d1 wip 2025-12-09 01:26:57 +05:00
967501d58f wip 2025-12-09 00:54:13 +05:00
88fdc3aa7f Update dependencies and refine Filament configuration
- Upgraded @tailwindcss/vite and tailwindcss to version 4.1.17 in package.json.
- Modified Vite configuration to include a new input file for Filament theme.
- Added AdvancedFileUpload component to VisaMasterPaymentOrderForm.
- Removed unused SpatieMediaLibraryFileUpload from VisaMasterPaymentOrderInfolist.
- Enhanced VisaMasterPaymentOrderInfolist schema for better layout and organization.
- Set Vite theme path in WorkPanelProvider for improved styling.
2025-12-08 21:47:06 +05:00
183916a62a add filament-upload-pro 2025-11-18 01:50:54 +05:00
a9490e132d wip 2025-11-18 01:46:06 +05:00
438e5a3f43 Add View Page for VisaMasterPaymentOrder and Update Schema
- Introduced ViewVisaMasterPaymentOrder page for displaying payment order details.
- Updated VisaMasterPaymentOrderResource to include a route for the new view page.
- Enhanced VisaMasterPaymentOrderInfolist schema to include location details and conditional visibility for notes.
- Enabled ViewAction in VisaMasterPaymentOrdersTable for accessing the new view page.
- Modified VisaMasterPaymentOrder model to allow nullable notes property.
2025-11-18 01:09:35 +05:00
cbd469c97f wip 2025-11-18 00:37:21 +05:00
bc45f2756e some stan 2025-11-18 00:34:40 +05:00
73448548e7 wip 2025-11-18 00:30:35 +05:00
f0e9767e78 wip 2025-11-18 00:23:04 +05:00
0e39450849 wip 2025-11-18 00:21:26 +05:00
4c83d41eb0 wip 2025-11-18 00:19:28 +05:00
bccf52a8fb wip 2025-11-18 00:19:04 +05:00
6e03c4d218 wip 2025-11-18 00:16:52 +05:00
53f8c893ca wip 2025-11-18 00:16:04 +05:00
52d070f4e5 wip 2025-11-18 00:13:08 +05:00
209f3899b5 wip 2025-11-18 00:11:11 +05:00
58d6ef0a9a Add navigation sort order to clusters and update profile panel sorting 2025-11-17 23:56:51 +05:00
1a0756fdca wip 2025-11-17 23:37:18 +05:00
f506972c91 wip 2025-11-16 18:05:56 +05:00
19abe08a90 wip 2025-11-16 18:04:18 +05:00
6428c9e20c wip 2025-11-16 17:29:03 +05:00
9d66f21854 wip 2025-11-16 17:27:33 +05:00
da5bb6e99b wip 2025-11-16 17:24:55 +05:00
680d5d4130 wip 2025-11-16 17:23:26 +05:00
f73a510984 wip 2025-11-16 17:20:40 +05:00
4b7c3da3d4 wip 2025-11-16 17:20:09 +05:00
b0877399eb wip 2025-11-16 17:17:23 +05:00
3b5b7f10c4 wip 2025-11-16 17:13:18 +05:00
45e95725cd wip 2025-11-16 17:09:54 +05:00
a98a41efab wip 2025-11-16 17:06:44 +05:00
3911e8e21a wip 2025-11-16 17:03:30 +05:00
dc871f36d4 wip 2025-11-16 17:00:58 +05:00
2dd06d0e4f wip 2025-11-16 16:50:55 +05:00
a9e5f7ece8 wip 2025-11-16 16:48:37 +05:00
7e7d583973 wip 2025-11-16 16:45:36 +05:00
ba6d703893 wip 2025-11-16 16:41:37 +05:00
2bb9ec2ae2 wip 2025-11-16 16:40:06 +05:00
1d67616cdb wip 2025-11-16 16:37:19 +05:00
5a37d56cfb wip 2025-11-16 16:31:16 +05:00
c1b3a58c3a wip 2025-11-16 00:52:35 +05:00
c94ac5d12d some stan 2025-11-15 21:32:03 +05:00
c24f7cbac6 Refactor payment processing: update method names for clarity, enhance VisaMasterPaymentOrderRepository with payment validation logic, and improve Turkish translations for payment notifications. 2025-11-15 18:51:40 +05:00
8637c22ed7 Refactor CurrencyRateForm and PayVisaMasterPaymentAction: streamline component definitions, enhance layout with fieldsets, and update Turkish translations for payment-related terms. 2025-11-14 18:29:14 +05:00
2659aae278 Refactor CurrencyRateResource and VisaMasterSettings: remove unnecessary whitespace, add property annotations, and define possible setting types. Update Turkish translations for warning text and content. 2025-11-13 22:46:19 +05:00
038b5ea2fb Add navigation sorting to SettingsCluster, enhance CurrencyRate model with table association and name attribute, and update Turkish translations for currency terms 2025-11-13 22:12:35 +05:00
54e11dcbb4 work on pay visa master action 2025-11-13 22:00:23 +05:00
3ea35f6bca currency rates 2025-11-13 21:51:43 +05:00
b67fd61bf8 wip 2025-11-13 21:48:38 +05:00
816a6c2f60 Refactor VisaMasterPaymentOrder resource: removed infolist method, updated header actions in edit and view pages, and improved infolist styling. 2025-11-13 21:17:20 +05:00
294ec0144d locale fix 2025-11-13 11:49:41 +05:00
c2e9198dbc wip 2025-11-13 01:14:53 +05:00
074fe44ab3 wip 2025-11-13 00:35:29 +05:00
31ab39b21c wip 2025-11-13 00:13:29 +05:00
7bdd5243bd wip 2025-11-10 19:53:22 +05:00
0da1efe5c3 wip 2025-11-10 19:18:53 +05:00
8c24f00382 wip 2025-11-10 17:58:06 +05:00
60f7efd6ba wip 2025-11-10 17:55:13 +05:00
c16bb98ed3 wip 2025-11-10 17:51:23 +05:00
0779e4b9f6 visa master basic form 2025-11-07 01:43:27 +05:00
9be16228f7 add resource for visamaster 2025-11-07 01:34:58 +05:00
84f551ddc6 add visa 2025-11-07 01:31:12 +05:00
96415c2bc1 Refactor getRedirectUrl method to ensure URL is treated as a string 2025-11-07 01:17:01 +05:00
345 changed files with 10811 additions and 15804 deletions

2
.gitignore vendored
View File

@@ -11,9 +11,9 @@
/.phpunit.cache
/.vscode
/.zed
/database/data/
/auth.json
/node_modules
/public/build
/public/hot
/public/storage
/storage/*.key

View File

@@ -23,7 +23,7 @@ class CreateCardOrder extends CreateRecord
protected function getRedirectUrl(): string
{
$defaultUrl = $this->getResource()::getUrl('index');
$defaultUrl = string($this->getResource()::getUrl('index'));
/** @var \App\Modules\CardOrder\Models\CardOrder */
$record = $this->record;

View File

@@ -3,8 +3,10 @@
namespace App\Filament\Clusters\Cards\CardOrders\Pages;
use App\Filament\Clusters\Cards\CardOrders\CardOrderResource;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
use Filament\Schemas\Components\Tabs\Tab;
class ListCardOrders extends ListRecords
{
@@ -16,4 +18,23 @@ class ListCardOrders extends ListRecords
CreateAction::make(),
];
}
public function getTabs(): array
{
if (! user()->isSystemUser()) {
return [];
}
$data = [];
foreach (array_keys(OrderStatusRepository::statusClasses()) as $status) {
if ($status === '') {
$data[null] = Tab::make(__('All'));
} else {
$data[$status] = Tab::make(OrderStatusRepository::statusFormatted($status))->query(fn ($query) => $query->where('status', $status));
}
}
return $data;
}
}

View File

@@ -26,11 +26,20 @@ class CardOrdersTable
->modifyQueryUsing(function (Builder $query) {
DefaultQueryForResourceIndexRepository::make($query);
})
->defaultSort('created_at', 'desc')
->columns([
TextColumn::make('unique_id')
->label(__('ID'))
->searchable(),
TextColumn::make('status')
->label(__('Status'))
->color(OrderStatusRepository::statusColorMatching())
->formatStateUsing(fn (string $state) => OrderStatusRepository::statusFormatted($state))
->badge()
->sortable()
->searchable(),
TextColumn::make('cardState.name')
->label(__('Reason'))
->searchable(),
@@ -69,14 +78,6 @@ class CardOrdersTable
->label(__('Phone'))
->searchable(),
TextColumn::make('status')
->label(__('Status'))
->color(OrderStatusRepository::statusColorMatching())
->formatStateUsing(fn (string $state) => OrderStatusRepository::statusFormatted($state))
->badge()
->sortable()
->searchable(),
TextColumn::make('updated_at')
->label(__('Updated At'))
->dateTime()

View File

@@ -5,15 +5,23 @@ namespace App\Filament\Clusters\Cards\Cards\Pages;
use App\Filament\Clusters\Cards\Cards\CardResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ManageRecords;
use Illuminate\Contracts\Support\Htmlable;
class ManageCards extends ManageRecords
{
protected static string $resource = CardResource::class;
public function getTitle(): string|Htmlable
{
return __('My cards');
}
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
CreateAction::make()
->label(__('Add card'))
->createAnother(false),
];
}
}

View File

@@ -9,6 +9,8 @@ use Filament\Support\Icons\Heroicon;
class CardsCluster extends Cluster
{
protected static ?int $navigationSort = 1;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedCreditCard;
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::Top;

View File

@@ -3,8 +3,10 @@
namespace App\Filament\Clusters\Cards\Resources\CardPinOrders\Pages;
use App\Filament\Clusters\Cards\Resources\CardPinOrders\CardPinOrderResource;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
use Filament\Schemas\Components\Tabs\Tab;
class ListCardPinOrders extends ListRecords
{
@@ -16,4 +18,23 @@ class ListCardPinOrders extends ListRecords
CreateAction::make(),
];
}
public function getTabs(): array
{
if (! user()->isSystemUser()) {
return [];
}
$data = [];
foreach (array_keys(OrderStatusRepository::statusClasses()) as $status) {
if ($status === '') {
$data[null] = Tab::make(__('All'));
} else {
$data[$status] = Tab::make(OrderStatusRepository::statusFormatted($status))->query(fn ($query) => $query->where('status', $status));
}
}
return $data;
}
}

View File

@@ -20,6 +20,7 @@ class CardPinOrdersTable
->modifyQueryUsing(function (Builder $query) {
DefaultQueryForResourceIndexRepository::make($query);
})
->defaultSort('created_at', 'desc')
->columns([
TextColumn::make('id')
->label('ID'),

View File

@@ -3,8 +3,10 @@
namespace App\Filament\Clusters\Loans\LoanOrders\Pages;
use App\Filament\Clusters\Loans\LoanOrders\LoanOrderResource;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
use Filament\Schemas\Components\Tabs\Tab;
class ListLoanOrders extends ListRecords
{
@@ -16,4 +18,23 @@ class ListLoanOrders extends ListRecords
CreateAction::make(),
];
}
public function getTabs(): array
{
if (! user()->isSystemUser()) {
return [];
}
$data = [];
foreach (array_keys(OrderStatusRepository::statusClasses()) as $status) {
if ($status === '') {
$data[null] = Tab::make(__('All'));
} else {
$data[$status] = Tab::make(OrderStatusRepository::statusFormatted($status))->query(fn ($query) => $query->where('status', $status));
}
}
return $data;
}
}

View File

@@ -25,6 +25,7 @@ class LoanOrdersTable
DefaultQueryForResourceIndexRepository::make($query);
})
->defaultSort('created_at', 'desc')
->columns([
TextColumn::make('id')
->label('ID')

View File

@@ -9,6 +9,8 @@ use Filament\Support\Icons\Heroicon;
class LoansCluster extends Cluster
{
protected static ?int $navigationSort = 2;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedBanknotes;
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::Top;

View File

@@ -3,8 +3,10 @@
namespace App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\Pages;
use App\Filament\Clusters\Loans\Resources\LoanOrderMobiles\LoanOrderMobileResource;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
use Filament\Schemas\Components\Tabs\Tab;
class ListLoanOrderMobiles extends ListRecords
{
@@ -16,4 +18,23 @@ class ListLoanOrderMobiles extends ListRecords
CreateAction::make(),
];
}
public function getTabs(): array
{
if (! user()->isSystemUser()) {
return [];
}
$data = [];
foreach (array_keys(OrderStatusRepository::statusClasses()) as $status) {
if ($status === '') {
$data[null] = Tab::make(__('All'));
} else {
$data[$status] = Tab::make(OrderStatusRepository::statusFormatted($status))->query(fn ($query) => $query->where('status', $status));
}
}
return $data;
}
}

View File

@@ -25,6 +25,7 @@ class LoanOrderMobilesTable
DefaultQueryForResourceIndexRepository::make($query);
})
->defaultSort('created_at', 'desc')
->columns([
TextColumn::make('id')
->label('ID')

View File

@@ -3,8 +3,10 @@
namespace App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\Pages;
use App\Filament\Clusters\Loans\Resources\LoanPaidOffLetters\LoanPaidOffLetterResource;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
use Filament\Schemas\Components\Tabs\Tab;
class ListLoanPaidOffLetters extends ListRecords
{
@@ -16,4 +18,23 @@ class ListLoanPaidOffLetters extends ListRecords
CreateAction::make(),
];
}
public function getTabs(): array
{
if (! user()->isSystemUser()) {
return [];
}
$data = [];
foreach (array_keys(OrderStatusRepository::statusClasses()) as $status) {
if ($status === '') {
$data[null] = Tab::make(__('All'));
} else {
$data[$status] = Tab::make(OrderStatusRepository::statusFormatted($status))->query(fn ($query) => $query->where('status', $status));
}
}
return $data;
}
}

View File

@@ -20,6 +20,7 @@ class LoanPaidOffLettersTable
->modifyQueryUsing(function (Builder $query) {
DefaultQueryForResourceIndexRepository::make($query);
})
->defaultSort('created_at', 'desc')
->columns([
TextColumn::make('id')
->label('ID')

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Filament\Clusters\OnlinePayments;
use BackedEnum;
use Filament\Clusters\Cluster;
use Filament\Pages\Enums\SubNavigationPosition;
use Filament\Support\Icons\Heroicon;
class OnlinePaymentsCluster extends Cluster
{
protected static ?int $navigationSort = 5;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedServerStack;
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::Top;
public static function getNavigationLabel(): string
{
return __('Online payments');
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments;
use App\Filament\Clusters\OnlinePayments\OnlinePaymentsCluster;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Pages\EditOnlinePayment;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Pages\ListOnlinePayments;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Schemas\OnlinePaymentForm;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Tables\OnlinePaymentsTable;
use App\Modules\OnlinePayment\Models\OnlinePayment;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
class OnlinePaymentResource extends Resource
{
protected static ?string $model = OnlinePayment::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
protected static ?string $cluster = OnlinePaymentsCluster::class;
protected static ?string $recordTitleAttribute = 'orderId';
public static function form(Schema $schema): Schema
{
return OnlinePaymentForm::configure($schema);
}
public static function table(Table $table): Table
{
return OnlinePaymentsTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListOnlinePayments::route('/'),
'edit' => EditOnlinePayment::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Pages;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\OnlinePaymentResource;
use Filament\Resources\Pages\CreateRecord;
class CreateOnlinePayment extends CreateRecord
{
protected static string $resource = OnlinePaymentResource::class;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Pages;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\OnlinePaymentResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditOnlinePayment extends EditRecord
{
protected static string $resource = OnlinePaymentResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Pages;
use App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\OnlinePaymentResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListOnlinePayments extends ListRecords
{
protected static string $resource = OnlinePaymentResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,157 @@
<?php
namespace App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Schemas;
use App\Modules\OnlinePayment\Repositories\OnlinePaymentRepository;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\Fieldset;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
class OnlinePaymentForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
Section::make()
->columnSpanFull()
->columns([
'default' => 1,
'md' => 2,
'xl' => 3,
])
->schema([
// Payment Information
Fieldset::make(__('Payment Information'))
->columns([
'default' => 1,
'md' => 2,
])
->schema([
TextInput::make('amount')
->label(__('Amount'))
->required()
->numeric()
->suffix('TMT')
->rules(['required', 'numeric', 'min:0']),
TextInput::make('description')
->label(__('Description'))
->required()
->maxLength(255),
TextInput::make('orderNumber')
->label(__('Order Number'))
->maxLength(255)
->helperText(__('Leave empty to auto-generate')),
Select::make('paymentStatus')
->label(__('Payment Status'))
->options(OnlinePaymentRepository::statusValues())
->default(OnlinePaymentRepository::PENDING)
->required(),
]),
// Card Information
Fieldset::make(__('Card Information'))
->columns([
'default' => 1,
'md' => 2,
])
->schema([
TextInput::make('cardholderName')
->label(__('Cardholder Name'))
->maxLength(255),
TextInput::make('pan')
->label(__('Card Number (PAN)'))
->maxLength(255)
->mask('9999 9999 9999 9999')
->placeholder('1234 5678 9012 3456'),
TextInput::make('expiration')
->label(__('Expiration Date'))
->mask('99/99')
->placeholder('MM/YY'),
TextInput::make('approvalCode')
->label(__('Approval Code'))
->maxLength(255),
]),
// Additional Information
Fieldset::make(__('Additional Information'))
->columns([
'default' => 1,
'md' => 2,
])
->schema([
TextInput::make('booking_number')
->label(__('Booking Number'))
->maxLength(255),
TextInput::make('depositedAmount')
->label(__('Deposited Amount'))
->numeric()
->suffix('TMT'),
TextInput::make('refunded_amount')
->label(__('Refunded Amount'))
->numeric()
->suffix('TMT'),
TextInput::make('username')
->label(__('Username'))
->maxLength(255)
->helperText(__('Payment provider username')),
]),
// URLs
Fieldset::make(__('URLs'))
->columns([
'default' => 1,
'md' => 2,
])
->schema([
TextInput::make('formUrl')
->label(__('Form URL'))
->url()
->maxLength(255),
TextInput::make('successUrl')
->label(__('Success URL'))
->url()
->maxLength(255),
TextInput::make('errorUrl')
->label(__('Error URL'))
->url()
->maxLength(255),
]),
// API Information
Fieldset::make(__('API Information'))
->columns([
'default' => 1,
'md' => 2,
])
->schema([
TextInput::make('api_client')
->label(__('API Client'))
->maxLength(255),
TextInput::make('callbackStatus')
->label(__('Callback Status'))
->maxLength(255),
TextInput::make('orderId')
->label(__('Order ID'))
->maxLength(255)
->helperText(__('External order identifier')),
]),
]),
]);
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace App\Filament\Clusters\OnlinePayments\Resources\OnlinePayments\Tables;
use App\Modules\OnlinePayment\Repositories\OnlinePaymentRepository;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class OnlinePaymentsTable
{
public static function configure(Table $table): Table
{
return $table
->defaultSort('created_at', direction: 'desc')
->columns([
TextColumn::make('id')
->label(__('ID'))
->sortable(),
TextColumn::make('orderNumber')
->label(__('Order Number'))
->searchable()
->sortable(),
TextColumn::make('amount')
->label(__('Amount'))
->money('TMT')
->sortable(),
TextColumn::make('description')
->label(__('Description'))
->limit(50),
TextColumn::make('paymentStatus')
->label(__('Payment Status'))
->badge()
->color(fn (string $state): string => match ($state) {
OnlinePaymentRepository::PAID => 'success',
OnlinePaymentRepository::FAILED => 'danger',
default => 'warning',
})
->formatStateUsing(fn (string $state): string => OnlinePaymentRepository::statusValues()[$state] ?? $state)
->sortable(),
TextColumn::make('cardholderName')
->label(__('Cardholder Name'))
->searchable()
->sortable(),
TextColumn::make('pan')
->label(__('Card Number'))
->formatStateUsing(fn (string $state): string => $state ? '**** **** **** '.substr($state, -4) : '-')
->searchable(),
TextColumn::make('orderId')
->label(__('Order ID'))
->searchable()
->sortable(),
TextColumn::make('username')
->label(__('Username'))
->searchable()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('created_at')
->label(__('Created At'))
->dateTime()
->sortable()
->toggleable(),
TextColumn::make('updated_at')
->label(__('Updated At'))
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\CurrencyRates;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\Pages\CreateCurrencyRate;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\Pages\EditCurrencyRate;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\Pages\ListCurrencyRates;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\Schemas\CurrencyRateForm;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\Tables\CurrencyRatesTable;
use App\Filament\Clusters\Settings\SettingsCluster;
use App\Modules\CurrencyRate\Models\CurrencyRate;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
class CurrencyRateResource extends Resource
{
protected static ?string $model = CurrencyRate::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedCurrencyDollar;
protected static ?string $cluster = SettingsCluster::class;
protected static ?string $recordTitleAttribute = 'name';
public static function getNavigationGroup(): ?string
{
return __('Currencies');
}
public static function getModelLabel(): string
{
return __('Currency rate');
}
public static function getPluralModelLabel(): string
{
return __('Currency rates');
}
public static function form(Schema $schema): Schema
{
return CurrencyRateForm::configure($schema);
}
public static function table(Table $table): Table
{
return CurrencyRatesTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListCurrencyRates::route('/'),
'create' => CreateCurrencyRate::route('/create'),
'edit' => EditCurrencyRate::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\CurrencyRates\Pages;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\CurrencyRateResource;
use Filament\Resources\Pages\CreateRecord;
class CreateCurrencyRate extends CreateRecord
{
protected static string $resource = CurrencyRateResource::class;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\CurrencyRates\Pages;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\CurrencyRateResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditCurrencyRate extends EditRecord
{
protected static string $resource = CurrencyRateResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\CurrencyRates\Pages;
use App\Filament\Clusters\Settings\Resources\CurrencyRates\CurrencyRateResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListCurrencyRates extends ListRecords
{
protected static string $resource = CurrencyRateResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\CurrencyRates\Schemas;
use App\Modules\CurrencyRate\Models\CurrencyRate;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
class CurrencyRateForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
Select::make('currency_from')
->label(__('Currency from'))
->native(false)
->searchable()
->options(CurrencyRate::currencies())
->rules('required')
->belowLabel('1 möçberi'),
Select::make('currency_to')
->label(__('Currency to'))
->native(false)
->searchable()
->options(CurrencyRate::currencies())
->rules('required')
->belowLabel('1 möçberi'),
TextInput::make('value')
->label(__('Currency value'))
->required()
->numeric()
->belowLabel('Bitin däl sanlary "." bilen ýazmaly'),
]);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\CurrencyRates\Tables;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class CurrencyRatesTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('currency_from')
->label(__('Currency from'))
->searchable(),
TextColumn::make('currency_to')
->label(__('Currency to'))
->searchable(),
TextColumn::make('value')
->label(__('Currency value'))
->searchable(),
TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
//
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Pages;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\VisaMasterSettingsResource;
use Filament\Resources\Pages\CreateRecord;
class CreateVisaMasterSettings extends CreateRecord
{
protected static string $resource = VisaMasterSettingsResource::class;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Pages;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\VisaMasterSettingsResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditVisaMasterSettings extends EditRecord
{
protected static string $resource = VisaMasterSettingsResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Pages;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\VisaMasterSettingsResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListVisaMasterSettings extends ListRecords
{
protected static string $resource = VisaMasterSettingsResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Schemas;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterSettings;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
class VisaMasterSettingsForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
Select::make('name')
->label(__('Type'))
->options(VisaMasterSettings::types())
->native(false)
->required(),
TextInput::make('display_name')
->label(__('Name'))
->required()
->string()
->maxLength(255)
->readonly(! user()->isMe()),
RichEditor::make('value')
->label(__('Content'))
->required()
->columnSpanFull(),
]);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Tables;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class VisaMasterSettingsTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('name')
->searchable(),
TextColumn::make('display_name')
->searchable(),
TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
//
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace App\Filament\Clusters\Settings\Resources\VisaMasterSettings;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Pages\CreateVisaMasterSettings;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Pages\EditVisaMasterSettings;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Pages\ListVisaMasterSettings;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Schemas\VisaMasterSettingsForm;
use App\Filament\Clusters\Settings\Resources\VisaMasterSettings\Tables\VisaMasterSettingsTable;
use App\Filament\Clusters\Settings\SettingsCluster;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterSettings;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
class VisaMasterSettingsResource extends Resource
{
protected static ?string $model = VisaMasterSettings::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedDocumentText;
protected static ?string $cluster = SettingsCluster::class;
protected static ?string $recordTitleAttribute = 'display_name';
public static function getNavigationGroup(): ?string
{
return __('Currencies');
}
public static function getModelLabel(): string
{
return __('Warning text');
}
public static function getPluralModelLabel(): string
{
return __('Warning text');
}
public static function form(Schema $schema): Schema
{
return VisaMasterSettingsForm::configure($schema);
}
public static function table(Table $table): Table
{
return VisaMasterSettingsTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListVisaMasterSettings::route('/'),
'create' => CreateVisaMasterSettings::route('/create'),
'edit' => EditVisaMasterSettings::route('/{record}/edit'),
];
}
}

View File

@@ -9,6 +9,8 @@ use Filament\Support\Icons\Heroicon;
class SettingsCluster extends Cluster
{
protected static ?int $navigationSort = 100;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedCog8Tooth;
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::End;

View File

@@ -9,6 +9,8 @@ use Filament\Support\Icons\Heroicon;
class UsersCluster extends Cluster
{
protected static ?int $navigationSort = 6;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedUsers;
protected static string|BackedEnum|null $activeNavigationIcon = Heroicon::Users;

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\VisaMasterPaymentOrderResource;
use Filament\Resources\Pages\CreateRecord;
class CreateVisaMasterPaymentOrder extends CreateRecord
{
protected static string $resource = VisaMasterPaymentOrderResource::class;
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\VisaMasterPaymentOrderResource;
use App\Modules\VisaMasterPaymentOrder\Filament\Actions\PayVisaMasterPaymentAction;
use Filament\Actions\DeleteAction;
use Filament\Actions\ForceDeleteAction;
use Filament\Actions\RestoreAction;
use Filament\Resources\Pages\EditRecord;
use Illuminate\Contracts\Support\Htmlable;
class EditVisaMasterPaymentOrder extends EditRecord
{
protected static string $resource = VisaMasterPaymentOrderResource::class;
protected function getHeaderActions(): array
{
return [
PayVisaMasterPaymentAction::make(),
DeleteAction::make(),
ForceDeleteAction::make(),
RestoreAction::make(),
];
}
public function getTitle(): string|Htmlable
{
return __('Order details');
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\VisaMasterPaymentOrderResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListVisaMasterPaymentOrders extends ListRecords
{
protected static string $resource = VisaMasterPaymentOrderResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Schemas\VisaMasterPaymentOrderInfolist;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\VisaMasterPaymentOrderResource;
use Filament\Actions\EditAction;
use Filament\Resources\Pages\ViewRecord;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
class ViewVisaMasterPaymentOrder extends ViewRecord
{
protected static string $resource = VisaMasterPaymentOrderResource::class;
protected function getHeaderActions(): array
{
return [
EditAction::make()
->icon(Heroicon::OutlinedPencil),
];
}
public function infolist(Schema $schema): Schema
{
return VisaMasterPaymentOrderInfolist::configure($schema);
}
}

View File

@@ -0,0 +1,263 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Schemas;
use App\Modules\FilamentPermission\Repositories\FilamentPermissionRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\PhoneNumberVerification\Rules\PhoneNumberVerificationRule;
use App\Modules\Region\Repositories\RegionRepository;
use App\Modules\TurkmenPassport\Repositories\TurkmenPassportRepository;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Fieldset;
use Filament\Schemas\Components\FusedGroup;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Wizard;
use Filament\Schemas\Components\Wizard\Step;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Illuminate\Support\Facades\Auth;
use RalphJSmit\Filament\Upload\Filament\Forms\Components\AdvancedFileUpload;
class VisaMasterPaymentOrderForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
Hidden::make('user_id')->default(Auth::id()),
Section::make()
->columnSpan(4)
->columns(4)
->disabled(fn (string $context): bool => FilamentPermissionRepository::forClients())
->components([
Select::make('status')
->label(__('Status'))
->options(OrderStatusRepository::statusValues())
->default(OrderStatusRepository::defaultStatus())
->native(false)
->columnSpan(2),
Toggle::make('paid')
->label(sprintf('%s (%s)', __('Paid'), __('This month')))
->inline(false)
->disabled(true)
->onIcon(Heroicon::CheckCircle)
->offIcon(Heroicon::XCircle)
->onColor('success')
->offColor('danger'),
RichEditor::make('notes')
->label(__('Bellik'))
->columnSpanFull(),
]),
Wizard::make([
Step::make(__('Order type and bank'))
->schema([
Fieldset::make(__('Order type'))
->schema([
Select::make('type')
->label(__('Type'))
->options(VisaMasterPaymentOrder::applicationTypes())
->native(false)
->required(),
]),
Fieldset::make(__('Location'))
->schema([
Select::make('region')
->label(__('Region'))
->options(RegionRepository::values())
->live()
->afterStateUpdated(fn (callable $set) => $set('branch_id', null))
->required(),
Select::make('branch_id')
->label(__('Branch'))
->relationship('branch', 'name', function ($query, callable $get) {
$query->orderByTranslation('name');
$region = $get('region');
if ($region) {
$query->where('region', $region);
}
})
->required(),
]),
]),
Step::make(__('Payment sender data'))
->columns(8)
->schema([
TextInput::make('sender_full_name')
->label(__('Name, Surname, Patronic name'))
->columnSpan(4)
->default(user()->fullName())
->required(),
FusedGroup::make([
Select::make('sender_passport_serie')
->label(__('Passport serie'))
->options(TurkmenPassportRepository::values())
->native(false)
->required()
->columnSpan(1)
->default(user()->getOption('passport_serie')),
TextInput::make('sender_passport_number')
->label(__('Passport number'))
->required()
->columnSpan(1)
->mask('999999')
->default(user()->getOption('passport_id')),
])
->columnSpan(2)
->label(__('Passport serie and number'))
->columns(2),
TextInput::make('phone')
->label(__('Phone'))
->required()
->mask('99 99 99 99')
->prefix('+993')
->rules([
new PhoneNumberVerificationRule,
])
->columnSpan(2)
->default(user()->phone),
TextInput::make('sender_deposit_account')
->label(__('Deposit account'))
->columnSpan(4)
->required(),
TextInput::make('address')
->label(__('Address'))
->columnSpan(4)
->required(),
Section::make(__('Files'))
->description('PNG, JPEG, PDF')
->columnSpanFull()
->columns(2)
->schema([
AdvancedFileUpload::make('sender_passport_local')
->spatieMediaLibrary(collection: 'sender_passport_local')
->multiple(),
SpatieMediaLibraryFileUpload::make('sender_passport_international')
->collection('sender_passport_international')
->label(__('Ugradyja degişli Türkmenistandan çykmak we Türkmenistana girmek üçin pasportynyň asyl görnüşi we göçürmesi')),
SpatieMediaLibraryFileUpload::make('sender_travel_stamp_on_passport')
->collection('sender_travel_stamp_on_passport')
->label(__('Ugradyja degişli Türkmenistandan çykmak we Türkmenistana girmek üçin pasportyndaky daşary döwletine gidendigi we daşary döwlete barandygy baradaky (ştampyň) bellenen sahypasynyň göçürmesi'))
->required(),
SpatieMediaLibraryFileUpload::make('sender_proof_of_kinship')
->collection('sender_proof_of_kinship')
->label(__('Ugradyjynyň we kabul edijiniň (talybyň) özara garyndaşlyk derejesini tassyklaýjy resminamalarynyň göçürmesi')),
SpatieMediaLibraryFileUpload::make('sender_passport_local_old')
->collection('sender_passport_local_old')
->label(__('Ugradyjy we kabul ediji (talyp) 2015-nji ýyldan soňra Türkmenistanyň raýatynyň pasportyny ikinji gezek alan bolsa, onda birinji gezek alan pasportynyň seriýasy baradaky maglumat')),
SpatieMediaLibraryFileUpload::make('sender_passport_local_old_replacement')
->collection('sender_passport_local_old_replacement')
->label(__('Ugradyjy we kabul ediji (talyp) 2015-nji ýyldan soňra Türkmenistanyň raýatynyň pasportyny ikinji gezek alandan soňra birinji gezek alan pasportynyň seriýasy baradaky maglumaty bilmeýän ,bolsa onda polisiýanyň degişli edaralaryndan birinji alan pasportynyň seriýasy baradaky güwänamasy')),
]),
]),
Step::make(__('Payee information'))
->columns(8)
->schema([
TextInput::make('reciever_full_name')
->label(__('Name, Surname, Patronic name'))
->columnSpan(5)
->required(),
FusedGroup::make([
Select::make('reciever_passport_serie')
->label(__('Passport serie'))
->options(TurkmenPassportRepository::values())
->native(false)
->required()
->columnSpan(1),
TextInput::make('reciever_passport_number')
->label(__('Passport number'))
->required()
->columnSpan(1)
->mask('999999'),
])
->columnSpan(3)
->label(__('Passport serie and number'))
->columns(2),
Section::make(__('Files'))
->description('PNG, JPEG, PDF')
->columnSpanFull()
->columns(2)
->schema([
SpatieMediaLibraryFileUpload::make('receiver_requisite')
->collection('receiver_requisite')
->label(__('Talyba degişli walýuta "VISA" kartyň rekwizitleri'))
->maxSize(4096)
->columnSpan(1)
->required(),
SpatieMediaLibraryFileUpload::make('receiver_document_stating_he_is_studying')
->collection('receiver_document_stating_he_is_studying')
->label(__('Talybyň daşary ýurt döwletiniň ýokary okuw mekdebinde okaýandygy baradaky güwänamasy'))
->maxSize(4096)
->openable()
->columnSpan(1),
SpatieMediaLibraryFileUpload::make('receiver_ticket')
->collection('receiver_ticket')
->label(__('Talybyň bilediniň göçürmesi'))
->maxSize(4096)
->columnSpan(1),
SpatieMediaLibraryFileUpload::make('receiver_passport_local')
->collection('receiver_passport_local')
->label(__('Talyba degişli Türkmenistanyň raýatynyň (içki milli) pasportynyň asyl görnüşi we göçürmesi'))
->maxSize(4096)
->columnSpan(1),
SpatieMediaLibraryFileUpload::make('receiver_passport_international')
->collection('receiver_passport_international')
->label(__('Talybyň Türkmenistandan çykmak we Türkmenistana girmek üçin (zagran) pasportynyň göçürmesi'))
->maxSize(4096)
->columnSpan(1),
SpatieMediaLibraryFileUpload::make('receiver_visa')
->collection('receiver_visa')
->label(__('Talybyň Türkmenistandan çykmak we Türkmenistana girmek üçin pasportyndaky daşary ýurtda galyp okap bilýändigi baradaky berlen möhleti hereket edýän rugsatnamasynyň (wizasynyň) bellenen sahypasynyň göçürmesi'))
->maxSize(4096)
->columnSpan(1),
SpatieMediaLibraryFileUpload::make('receiver_travel_stamp_on_passport')
->collection('receiver_travel_stamp_on_passport')
->label(__('Talybyň Türkmenistandan çykmak we Türkmenistana girmek üçin pasportyndaky Türkmenistandan çykandygy we daşary ýurt döwletine girendigi baradaky ştamplaryň (seneli ştampyň) bellenen sahypasynyň göçürmesi'))
->maxSize(4096)
->columnSpan(1),
SpatieMediaLibraryFileUpload::make('receiver_document_stating_he_is_studying_2')
->collection('receiver_document_stating_he_is_studying_2')
->label(__('Talybyň daşary ýurt döwletiniň ýokary okuw mekdebinde okaýandygy baradaky güwänamasyndaky maglumatyň doly takyk däl ýagdaýynda takyk däl maglumatyň sebäpleri baradaky daşary ýurt döwletiniň ýokary okuw mekdebinden haty'))
->maxSize(4096)
->columnSpan(1),
]),
]),
])->columnSpanFull()->skippable(fn (string $context) => $context === 'edit'),
]);
}
}

View File

@@ -0,0 +1,212 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Schemas;
use App\Filament\Infolists\Components\SpatieMediaLibraryFileEntry;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\Region\Repositories\RegionRepository;
use App\Modules\TurkmenPassport\Repositories\TurkmenPassportRepository;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder;
use Filament\Infolists\Components\IconEntry;
use Filament\Infolists\Components\TextEntry;
use Filament\Schemas\Components\Fieldset;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Illuminate\Support\Str;
class VisaMasterPaymentOrderInfolist
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
Section::make()
->columnSpanFull()
->columns(4)
->components([
TextEntry::make('unique_id')
->label(__('Order ID'))
->columnSpan(1)
->extraAttributes(['style' => 'text-transform:uppercase;font-size:1.5em;font-weight:bold;']),
TextEntry::make('type')
->label(__('Payment type'))
->extraAttributes(['style' => 'text-transform:uppercase;font-size:1.5em;font-weight:bold;']),
TextEntry::make('status')
->label(__('Status'))
->formatStateUsing(fn (string $state) => OrderStatusRepository::statusFormatted($state))
->extraAttributes(['style' => 'text-transform:uppercase;font-size:1.2em;font-weight:bold;'])
->color(OrderStatusRepository::statusColorMatching()),
IconEntry::make('paid')
->label(sprintf('%s (%s)', __('Paid'), __('This month')))
->boolean()
->trueIcon(Heroicon::CheckCircle)
->falseIcon(Heroicon::XCircle)
->trueColor('success')
->falseColor('danger'),
Fieldset::make(__('Location'))
->schema([
TextEntry::make('region')
->label(__('Region'))
->formatStateUsing(fn (string $state): string => RegionRepository::label($state)),
TextEntry::make('branch.name')
->label(__('Branch'))
->placeholder('-'),
])
->columnSpan(2),
TextEntry::make('notes')
->label(__('Bellik'))
->html()
->visible(fn (VisaMasterPaymentOrder $record): bool => ! is_null($record->notes) && $record->notes != '' && $record->notes != '<p></p>' && Str::length($record->notes) > 7)
->columnSpan(2)
->placeholder('-'),
]),
Tabs::make('Order Information')
->tabs([
Tab::make(__('Payment sender data'))
->columns(8)
->schema([
Fieldset::make(__('Data'))
->columnSpan(8)
->columns(8)
->schema([
TextEntry::make('sender_full_name')
->label(__('Name, Surname, Patronic name'))
->columnSpan(4)
->placeholder('-'),
TextEntry::make('sender_passport')
->label(__('Passport serie and number'))
->formatStateUsing(function ($record) {
$serie = TurkmenPassportRepository::values()[$record->sender_passport_serie] ?? $record->sender_passport_serie;
return $serie.' '.$record->sender_passport_number;
})
->columnSpan(2)
->placeholder('-'),
TextEntry::make('phone')
->label(__('Phone'))
->formatStateUsing(fn ($state) => '+993 '.$state)
->columnSpan(2)
->placeholder('-'),
TextEntry::make('sender_deposit_account')
->label(__('Deposit account'))
->columnSpan(4)
->placeholder('-'),
TextEntry::make('address')
->label(__('Address'))
->columnSpan(4)
->placeholder('-'),
]),
Section::make(__('Files'))
->description('PNG, JPEG, PDF')
->columnSpanFull()
->columns(2)
->schema([
SpatieMediaLibraryFileEntry::make('sender_passport_local')
->collection('sender_passport_local')
->label(__('Ugradyja degişli Türkmenistanyň raýatynyň (içki milli) pasportynyň asyl görnüşi we göçürmesi')),
SpatieMediaLibraryFileEntry::make('sender_passport_international')
->collection('sender_passport_international')
->label(__('Ugradyja degişli Türkmenistandan çykmak we Türkmenistana girmek üçin pasportynyň asyl görnüşi we göçürmesi')),
SpatieMediaLibraryFileEntry::make('sender_travel_stamp_on_passport')
->collection('sender_travel_stamp_on_passport')
->label(__('Ugradyja degişli Türkmenistandan çykmak we Türkmenistana girmek üçin pasportyndaky daşary döwletine gidendigi we daşary döwlete barandygy baradaky (ştampyň) bellenen sahypasynyň göçürmesi')),
SpatieMediaLibraryFileEntry::make('sender_proof_of_kinship')
->collection('sender_proof_of_kinship')
->label(__('Ugradyjynyň we kabul edijiniň (talybyň) özara garyndaşlyk derejesini tassyklaýjy resminamalarynyň göçürmesi')),
SpatieMediaLibraryFileEntry::make('sender_passport_local_old')
->collection('sender_passport_local_old')
->label(__('Ugradyjy we kabul ediji (talyp) 2015-nji ýyldan soňra Türkmenistanyň raýatynyň pasportyny ikinji gezek alan bolsa, onda birinji gezek alan pasportynyň seriýasy baradaky maglumat')),
SpatieMediaLibraryFileEntry::make('sender_passport_local_old_replacement')
->collection('sender_passport_local_old_replacement')
->label(__('Ugradyjy we kabul ediji (talyp) 2015-nji ýyldan soňra Türkmenistanyň raýatynyň pasportyny ikinji gezek alandan soňra birinji gezek alan pasportynyň seriýasy baradaky maglumaty bilmeýän ,bolsa onda polisiýanyň degişli edaralaryndan birinji alan pasportynyň seriýasy baradaky güwänamasy')),
]),
]),
Tab::make(__('Payee information'))
->columns(8)
->schema([
TextEntry::make('reciever_full_name')
->label(__('Name, Surname, Patronic name'))
->columnSpan(5)
->placeholder('-'),
TextEntry::make('reciever_passport')
->label(__('Passport serie and number'))
->formatStateUsing(function ($record) {
$serie = TurkmenPassportRepository::values()[$record->reciever_passport_serie] ?? $record->reciever_passport_serie;
return $serie.' '.$record->reciever_passport_number;
})
->columnSpan(3)
->placeholder('-'),
Section::make(__('Files'))
->description('PNG, JPEG, PDF')
->columnSpanFull()
->columns(2)
->schema([
SpatieMediaLibraryFileEntry::make('receiver_requisite')
->collection('receiver_requisite')
->label(__('Talyba degişli walýuta "VISA" kartyň rekwizitleri'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_document_stating_he_is_studying')
->collection('receiver_document_stating_he_is_studying')
->label(__('Talybyň daşary ýurt döwletiniň ýokary okuw mekdebinde okaýandygy baradaky güwänamasy'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_ticket')
->collection('receiver_ticket')
->label(__('Talybyň bilediniň göçürmesi'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_passport_local')
->collection('receiver_passport_local')
->label(__('Talyba degişli Türkmenistanyň raýatynyň (içki milli) pasportynyň asyl görnüşi we göçürmesi'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_passport_international')
->collection('receiver_passport_international')
->label(__('Talybyň Türkmenistandan çykmak we Türkmenistana girmek üçin (zagran) pasportynyň göçürmesi'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_visa')
->collection('receiver_visa')
->label(__('Talybyň Türkmenistandan çykmak we Türkmenistana girmek üçin pasportyndaky daşary ýurtda galyp okap bilýändigi baradaky berlen möhleti hereket edýän rugsatnamasynyň (wizasynyň) bellenen sahypasynyň göçürmesi'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_travel_stamp_on_passport')
->collection('receiver_travel_stamp_on_passport')
->label(__('Talybyň Türkmenistandan çykmak we Türkmenistana girmek üçin pasportyndaky Türkmenistandan çykandygy we daşary ýurt döwletine girendigi baradaky ştamplaryň (seneli ştampyň) bellenen sahypasynyň göçürmesi'))
->columnSpan(1),
SpatieMediaLibraryFileEntry::make('receiver_document_stating_he_is_studying_2')
->collection('receiver_document_stating_he_is_studying_2')
->label(__('Talybyň daşary ýurt döwletiniň ýokary okuw mekdebinde okaýandygy baradaky güwänamasyndaky maglumatyň doly takyk däl ýagdaýynda takyk däl maglumatyň sebäpleri baradaky daşary ýurt döwletiniň ýokary okuw mekdebinden haty'))
->columnSpan(1),
]),
]),
])->columnSpanFull(),
]);
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Tables;
use App\Modules\DefaultQueryForResourceIndex\Repositories\DefaultQueryForResourceIndexRepository;
use App\Modules\OrderStatus\Repositories\OrderStatusRepository;
use App\Modules\Region\Repositories\RegionRepository;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Actions\ForceDeleteBulkAction;
use Filament\Actions\RestoreBulkAction;
use Filament\Actions\ViewAction;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Str;
class VisaMasterPaymentOrdersTable
{
public static function configure(Table $table): Table
{
return $table
->modifyQueryUsing(function (Builder $query) {
DefaultQueryForResourceIndexRepository::make($query);
})
->defaultSort('created_at', direction: 'desc')
->columns([
TextColumn::make('id')
->label(__('ID'))
->sortable(),
TextColumn::make('type')
->label(__('Type'))
->formatStateUsing(fn (string $state) => Str::upper($state)),
TextColumn::make('status')
->label(__('Status'))
->color(OrderStatusRepository::statusColorMatching())
->formatStateUsing(fn (string $state) => OrderStatusRepository::statusFormatted($state))
->badge(),
TextColumn::make('created_at')
->label(__('Created At'))
->dateTime()
->sortable(),
TextColumn::make('region')
->label(__('Region'))
->formatStateUsing(fn (string $state): string => RegionRepository::label($state))
->searchable(),
TextColumn::make('branch.name')
->label(__('Branch'))
->searchable(),
TextColumn::make('sender_full_name')
->label(__('Full Name'))
->searchable(),
TextColumn::make('phone')
->label(__('Phone'))
->searchable(),
IconColumn::make('paid')
->label(sprintf('%s (%s)', __('Paid'), __('This month')))
->boolean(),
])
->filters([
TrashedFilter::make(),
])
->recordActions([
ViewAction::make(),
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
ForceDeleteBulkAction::make(),
RestoreBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages\CreateVisaMasterPaymentOrder;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages\EditVisaMasterPaymentOrder;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages\ListVisaMasterPaymentOrders;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Pages\ViewVisaMasterPaymentOrder;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Schemas\VisaMasterPaymentOrderForm;
use App\Filament\Clusters\VisaMasterPayments\Resources\VisaMasterPaymentOrders\Tables\VisaMasterPaymentOrdersTable;
use App\Filament\Clusters\VisaMasterPayments\VisaMasterPaymentsCluster;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class VisaMasterPaymentOrderResource extends Resource
{
protected static ?int $navigationSort = 1;
protected static ?string $model = VisaMasterPaymentOrder::class;
protected static ?string $cluster = VisaMasterPaymentsCluster::class;
protected static ?string $recordTitleAttribute = 'unique_id';
protected static string|BackedEnum|null $navigationIcon = 'icon-visa-plain';
public static function getNavigationGroup(): ?string
{
return __('Visa/Master payments');
}
public static function getModelLabel(): string
{
return __('Visa/Master payment');
}
public static function getPluralModelLabel(): string
{
return __('Visa/Master payments');
}
public static function form(Schema $schema): Schema
{
return VisaMasterPaymentOrderForm::configure($schema);
}
public static function table(Table $table): Table
{
return VisaMasterPaymentOrdersTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListVisaMasterPaymentOrders::route('/'),
'create' => CreateVisaMasterPaymentOrder::route('/create'),
'view' => ViewVisaMasterPaymentOrder::route('/{record}/view'),
'edit' => EditVisaMasterPaymentOrder::route('/{record}/edit'),
];
}
/**
* Get the query for the record route binding
*
* @return \Illuminate\Database\Eloquent\Builder<\App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder>
*/
public static function getRecordRouteBindingEloquentQuery(): Builder
{
return parent::getRecordRouteBindingEloquentQuery()
->withoutGlobalScopes([
SoftDeletingScope::class,
]);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Filament\Clusters\VisaMasterPayments;
use BackedEnum;
use Filament\Clusters\Cluster;
use Filament\Pages\Enums\SubNavigationPosition;
class VisaMasterPaymentsCluster extends Cluster
{
protected static ?int $navigationSort = 3;
protected static string|BackedEnum|null $navigationIcon = 'icon-visa-plain';
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::Top;
public static function getNavigationLabel(): string
{
return __('International payments');
}
}

View File

@@ -0,0 +1,174 @@
<?php
namespace App\Filament\Infolists\Components;
use Closure;
use Filament\Actions\Action;
use Filament\Infolists\Components\Entry;
use Filament\Support\Concerns\HasMediaFilter;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Spatie\MediaLibrary\MediaCollections\Models\Collections\MediaCollection;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Throwable;
class SpatieMediaLibraryFileEntry extends Entry
{
use HasMediaFilter;
public function getViewMediaActionName(): string
{
return 'view_media_'.$this->getName();
}
protected function setUp(): void
{
parent::setUp();
$this->registerActions([
Action::make($this->getViewMediaActionName())
->label(__('Watch Full'))
->modalContent(fn (array $arguments) => view('filament.infolists.components.image-modal', ['url' => $arguments['url']]))
->modalSubmitAction(false)
->modalCancelAction(false)
->modalWidth('5xl'),
]);
}
protected string $view = 'filament.infolists.components.spatie-media-library-file-entry';
protected string|Closure|null $collection = null;
protected string|Closure|null $conversion = null;
protected string|Closure $visibility = 'private';
protected bool|Closure $isDownloadable = true;
protected bool|Closure $isPreviewable = true;
public function collection(string|Closure|null $collection): static
{
$this->collection = $collection;
return $this;
}
public function conversion(string|Closure|null $conversion): static
{
$this->conversion = $conversion;
return $this;
}
public function visibility(string|Closure $visibility): static
{
$this->visibility = $visibility;
return $this;
}
public function downloadable(bool|Closure $condition = true): static
{
$this->isDownloadable = $condition;
return $this;
}
public function previewable(bool|Closure $condition = true): static
{
$this->isPreviewable = $condition;
return $this;
}
public function getCollection(): ?string
{
return $this->evaluate($this->collection);
}
public function getConversion(): ?string
{
return $this->evaluate($this->conversion);
}
public function getVisibility(): string
{
return (string) $this->evaluate($this->visibility);
}
public function isDownloadable(): bool
{
return (bool) $this->evaluate($this->isDownloadable);
}
public function isPreviewable(): bool
{
return (bool) $this->evaluate($this->isPreviewable);
}
public function getMediaUrl(Media $media, ?string $conversion = null): string
{
if ($this->getVisibility() === 'private') {
try {
return $media->getTemporaryUrl(
now()->addMinutes(30)->endOfHour(),
$conversion ?? '',
);
} catch (Throwable $exception) {
// This driver does not support creating temporary URLs.
}
}
return $media->getUrl($conversion ?? '');
}
/**
* Get the media for the entry
*
* @return Collection<array-key, Media>
*/
public function getMedia(): Collection
{
$record = $this->getRecord();
if (! $record) {
return collect([]);
}
if ($this->hasStateRelationship($record)) {
$record = $this->getStateRelationshipResults($record);
}
$records = Arr::wrap($record);
$allMedia = collect([]);
$collection = $this->getCollection() ?? 'default';
foreach ($records as $record) {
/** @var Model $record */
$media = $record->getRelationValue('media');
if (! $media) {
continue;
}
$filteredMedia = $media
->when(
is_string($collection),
fn (MediaCollection $mediaCollection) => $mediaCollection->filter(fn (Media $media): bool => $media->getAttributeValue('collection_name') === $collection),
)
->when(
$this->hasMediaFilter(),
fn (Collection $media) => $this->filterMedia($media)
)
->sortBy('order_column');
$allMedia = $allMedia->merge($filteredMedia);
}
return $allMedia;
}
}

View File

@@ -32,6 +32,7 @@ class UserForm
->unique(ignoreRecord: true)
->mask('99 99 99 99')
->prefix('+993')
->dehydrateStateUsing(fn ($state) => unMaskTurkmenNumber($state))
->rules([
new PhoneNumberVerificationRule,
])

View File

@@ -0,0 +1,69 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Database\Seeders\Migrators\ActionEventsMigrator;
use Database\Seeders\Migrators\BranchUserMigrator;
use Database\Seeders\Migrators\CurrencyRatesMigrator;
use Database\Seeders\Migrators\LoanOrderRequiredDocsMigrator;
use Database\Seeders\Migrators\LoanOrdersMigrator;
use Database\Seeders\Migrators\LoanTypesMigrator;
use Database\Seeders\Migrators\MediaMigrator;
use Database\Seeders\Migrators\ProvincesMigrator;
use Database\Seeders\Migrators\BranchesMigrator;
use Database\Seeders\Migrators\UsersMigrator;
use Database\Seeders\Migrators\CardStatesMigrator;
use Database\Seeders\Migrators\CardTypesMigrator;
use Database\Seeders\Migrators\VisaMasterPaymentOrdersMigrator;
use Database\Seeders\Migrators\VerificationsMigrator;
use Database\Seeders\Migrators\PersonalAccessTokensMigrator;
use Database\Seeders\Migrators\CardOrdersMigrator;
use Database\Seeders\Migrators\VisaMasterSettingsMigrator;
use Database\Seeders\Migrators\CardPinOrdersMigrator;
use Database\Seeders\Migrators\ModelHasRolesMigrator;
use Database\Seeders\Migrators\OnlinePaymentsMigrator;
class MigrationController extends Controller
{
public function index()
{
return $this->test();
$migrators = [
new ActionEventsMigrator(),
new UsersMigrator(),
new ProvincesMigrator(),
new BranchesMigrator(),
new BranchUserMigrator(),
new CardStatesMigrator(),
new CardTypesMigrator(),
new VerificationsMigrator(),
new CurrencyRatesMigrator(),
new LoanOrderRequiredDocsMigrator(),
new PersonalAccessTokensMigrator(),
new LoanTypesMigrator(),
new CardOrdersMigrator(),
new CardPinOrdersMigrator(),
new LoanOrdersMigrator(),
new ModelHasRolesMigrator(),
new VisaMasterPaymentOrdersMigrator(),
new OnlinePaymentsMigrator(),
new MediaMigrator(),
];
foreach ($migrators as $migrator) {
$migrator->migrate();
}
return 'done';
}
public function test()
{
(new OnlinePaymentsMigrator())->migrate();
return 'done';
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserHasRole
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
// if user does not have any role, add role "client"
/** @var \App\Models\User */
$user = $request->user();
if ($user->roles->count() == 0) {
$user->assignRole('client');
}
return $next($request);
}
}

View File

@@ -4,11 +4,13 @@ namespace App\Models;
use App\Modules\UserAdjustments\Traits\UserAdjustments;
use Filament\Models\Contracts\FilamentUser;
use Filament\Models\Contracts\HasAvatar;
use Filament\Panel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Date;
use Laravel\Sanctum\HasApiTokens;
/**
* @property int $id
@@ -20,13 +22,14 @@ use Illuminate\Support\Facades\Date;
* @property Date|null $created_at
* @property Date|null $updated_at
*/
class User extends Authenticatable implements FilamentUser
class User extends Authenticatable implements FilamentUser, HasAvatar
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory;
use Notifiable;
use UserAdjustments;
use HasApiTokens;
/**
* The attributes that are mass assignable.
@@ -56,4 +59,12 @@ class User extends Authenticatable implements FilamentUser
{
return true;
}
/**
* Get the avatar URL for the user.
*/
public function getFilamentAvatarUrl(): ?string
{
return '/assets/images/avatar.png';
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Modules\ActivityLog;
use App\Modules\Makeable;
use App\Modules\ModuleContract;
class ActivityLogModule implements ModuleContract
{
use Makeable;
/**
* Module is enabled
*/
protected bool $enabled = true;
/**
* Check if is module enabled
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Disable module
*/
public function disable(): void
{
$this->enabled = false;
}
/**
* Enable module
*/
public function enable(): void
{
$this->enabled = true;
}
/**
* Check if module has a filament resource
*/
public function hasFilamentResource(): bool
{
return false;
}
/**
* Get module composer requirements
*/
public function getComposerRequirements(): array
{
return [];
}
/**
* Get module composer suggestions
*/
public function getComposerSuggestions(): array
{
return [];
}
}

View File

@@ -0,0 +1,5 @@
<?php
namespace App\Modules\ActivityLog\Repositories;
class ActivityLogRepository {}

View File

@@ -14,6 +14,7 @@ use Illuminate\Http\Response;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rule;
class LoginController extends Controller
{
@@ -113,7 +114,7 @@ class LoginController extends Controller
protected function validateLogin(Request $request): void
{
$request->validate([
$this->username() => ['required', 'string', 'max:250'],
$this->username() => ['required', 'string', 'max:250', Rule::notIn('65999990', 'ulanyjy_ady')],
'password' => ['required', 'string', 'max:250'],
]);
}

View File

@@ -30,4 +30,5 @@ return [
'go_back' => 'Yza',
'successfully_changed_phone' => 'Telefon belgiňiz üýtgedildi',
'resend' => 'Täze tassyklaýyş belgi ugratmak',
'phone_or_username_placeholder' => 'meselem: 65999990 yada ulanyjy_ady',
];

View File

@@ -49,11 +49,12 @@ async function login(event) {
id="username"
type="text"
name="username"
placeholder="+99365999990 {{ __('or') }} {{ __('module.base-auth::base.username') }}"
placeholder="65999990 {{ __('or') }} {{ __('module.base-auth::base.username') }}"
autofocus=""
value="{{ old('username') }}"
>
<span class="text-gray-500 text-xs">{{ __('module.base-auth::base.phone_or_username_placeholder') }}</span>
<span id="username-error-box" class="text-red-500 text-italic error-box"></span>
</div>

View File

@@ -109,7 +109,16 @@ class CardOrder extends Model implements BelongsToBranch, HasStatus
parent::boot();
static::creating(LoanOrderRepository::creating());
static::created(LoanOrderRepository::created());
static::created(function ($model) {
$uniqueId = LoanOrderRepository::generateUniqueId($model);
$model->update(['unique_id' => $uniqueId]);
sendSMS(
$model->phone,
__('module.card-order::base.card_order_created', ['order_id' => $uniqueId])
);
});
}
/**

View File

@@ -19,8 +19,8 @@ class CardOrderRepository
/** @var \App\Modules\Branch\Models\Branch */
$branch = $record->branch;
return OnlinePaymentRepository::make(relatedModel: $record)
->paymentProvider(
return OnlinePaymentRepository::make(relatedModel: $record, apiClient: 'billing')
->setPaymentProvider(
HalkbankOnlinePaymentRepository::make()
->setUsername($branch->billingUsername())
->setPassword($branch->billingPassword())

View File

@@ -0,0 +1,5 @@
<?php
return [
'card_order_created' => 'Kart sargydyňyz :order_id belgi bilen hasaba alyndy. Sargydy tassyklamak üçin operatorymyz habarlaşar.',
];

View File

@@ -0,0 +1,49 @@
<?php
namespace App\Modules\CurrencyRate\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class CurrencyRateController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request): void
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): void
{
//
}
/**
* Display the specified resource.
*/
public function show(Request $request): void
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request): void
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Request $request): void
{
//
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Modules\CurrencyRate;
use App\Modules\Makeable;
use App\Modules\ModuleContract;
class CurrencyRateModule implements ModuleContract
{
use Makeable;
/**
* Module is enabled
*/
protected bool $enabled = true;
/**
* Check if is module enabled
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Disable module
*/
public function disable(): void
{
$this->enabled = false;
}
/**
* Enable module
*/
public function enable(): void
{
$this->enabled = true;
}
/**
* Check if module has a filament resource
*/
public function hasFilamentResource(): bool
{
return false;
}
/**
* Get module composer requirements
*/
public function getComposerRequirements(): array
{
return [];
}
/**
* Get module composer suggestions
*/
public function getComposerSuggestions(): array
{
return [];
}
}

View File

@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('currency_rates', function (Blueprint $table) {
$table->id();
$table->string('currency_from');
$table->string('currency_to');
$table->string('value');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('currency_rates');
}
};

View File

@@ -0,0 +1,217 @@
<?php
namespace App\Modules\CurrencyRate\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string $currency_from
* @property string $currency_to
* @property string $value
* @property \Illuminate\Support\Carbon $created_at
* @property \Illuminate\Support\Carbon $updated_at
*/
class CurrencyRate extends Model
{
/**
* The table associated with the model.
*/
protected $table = 'currency_rates';
/**
* Get the user's first name.
*
* @return Attribute<string, void>
*/
protected function name(): Attribute
{
return Attribute::make(
get: fn (): string => $this->currency_from.'-'.$this->currency_to,
);
}
/**
* Currencies
*
* @return array<string, string>
*/
public static function currencies(): array
{
return [
'AED' => 'AED',
'AFN' => 'AFN',
'ALL' => 'ALL',
'AMD' => 'AMD',
'ANG' => 'ANG',
'AOA' => 'AOA',
'ARS' => 'ARS',
'AUD' => 'AUD',
'AWG' => 'AWG',
'AZN' => 'AZN',
'BAM' => 'BAM',
'BBD' => 'BBD',
'BDT' => 'BDT',
'BGN' => 'BGN',
'BHD' => 'BHD',
'BIF' => 'BIF',
'BMD' => 'BMD',
'BND' => 'BND',
'BOB' => 'BOB',
'BRL' => 'BRL',
'BSD' => 'BSD',
'BTN' => 'BTN',
'BWP' => 'BWP',
'BYN' => 'BYN',
'BZD' => 'BZD',
'CAD' => 'CAD',
'CDF' => 'CDF',
'CHF' => 'CHF',
'CKD' => 'CKD',
'CLP' => 'CLP',
'CNY' => 'CNY',
'COP' => 'COP',
'CRC' => 'CRC',
'CUC' => 'CUC',
'CUP' => 'CUP',
'CVE' => 'CVE',
'CZK' => 'CZK',
'DJF' => 'DJF',
'DKK' => 'DKK',
'DOP' => 'DOP',
'DZD' => 'DZD',
'EGP' => 'EGP',
'EHP' => 'EHP',
'ERN' => 'ERN',
'ETB' => 'ETB',
'EUR' => 'EUR',
'FJD' => 'FJD',
'FKP' => 'FKP',
'FOK' => 'FOK',
'GBP' => 'GBP',
'GEL' => 'GEL',
'GGP' => 'GGP',
'GHS' => 'GHS',
'GIP' => 'GIP',
'GMD' => 'GMD',
'GNF' => 'GNF',
'GTQ' => 'GTQ',
'GYD' => 'GYD',
'HKD' => 'HKD',
'HNL' => 'HNL',
'HRK' => 'HRK',
'HTG' => 'HTG',
'HUF' => 'HUF',
'IDR' => 'IDR',
'ILS' => 'ILS',
'IMP' => 'IMP',
'INR' => 'INR',
'IQD' => 'IQD',
'IRR' => 'IRR',
'ISK' => 'ISK',
'JEP' => 'JEP',
'JMD' => 'JMD',
'JOD' => 'JOD',
'JPY' => 'JPY',
'KES' => 'KES',
'KGS' => 'KGS',
'KHR' => 'KHR',
'KID' => 'KID',
'KMF' => 'KMF',
'KPW' => 'KPW',
'KRW' => 'KRW',
'KWD' => 'KWD',
'KYD' => 'KYD',
'KZT' => 'KZT',
'LAK' => 'LAK',
'LBP' => 'LBP',
'LKR' => 'LKR',
'LRD' => 'LRD',
'LSL' => 'LSL',
'LYD' => 'LYD',
'MAD' => 'MAD',
'MDL' => 'MDL',
'MGA' => 'MGA',
'MKD' => 'MKD',
'MMK' => 'MMK',
'MNT' => 'MNT',
'MOP' => 'MOP',
'MRU' => 'MRU',
'MUR' => 'MUR',
'MVR' => 'MVR',
'MWK' => 'MWK',
'MXN' => 'MXN',
'MYR' => 'MYR',
'MZN' => 'MZN',
'NAD' => 'NAD',
'NGN' => 'NGN',
'NIO' => 'NIO',
'NOK' => 'NOK',
'NPR' => 'NPR',
'NZD' => 'NZD',
'OMR' => 'OMR',
'PAB' => 'PAB',
'PEN' => 'PEN',
'PGK' => 'PGK',
'PHP' => 'PHP',
'PKR' => 'PKR',
'PLN' => 'PLN',
'PND' => 'PND',
'PRB' => 'PRB',
'PYG' => 'PYG',
'QAR' => 'QAR',
'RON' => 'RON',
'RSD' => 'RSD',
'RUB' => 'RUB',
'RWF' => 'RWF',
'SAR' => 'SAR',
'SBD' => 'SBD',
'SCR' => 'SCR',
'SDG' => 'SDG',
'SEK' => 'SEK',
'SGD' => 'SGD',
'SHP' => 'SHP',
'SLL' => 'SLL',
'SLS' => 'SLS',
'SOS' => 'SOS',
'SRD' => 'SRD',
'SSP' => 'SSP',
'STN' => 'STN',
'SVC' => 'SVC',
'SYP' => 'SYP',
'SZL' => 'SZL',
'THB' => 'THB',
'TJS' => 'TJS',
'TMT' => 'TMT',
'TND' => 'TND',
'TOP' => 'TOP',
'TRY' => 'TRY',
'TTD' => 'TTD',
'TVD' => 'TVD',
'TWD' => 'TWD',
'TZS' => 'TZS',
'UAH' => 'UAH',
'UGX' => 'UGX',
'USD' => 'USD',
'UYU' => 'UYU',
'UZS' => 'UZS',
'VED' => 'VED',
'VES' => 'VES',
'VND' => 'VND',
'VUV' => 'VUV',
'WST' => 'WST',
'XAF' => 'XAF',
'XCD' => 'XCD',
'XOF' => 'XOF',
'XPF' => 'XPF',
'YER' => 'YER',
'ZAR' => 'ZAR',
'ZMW' => 'ZMW',
'ZWB' => 'ZWB',
'ZWL' => 'ZWL',
'Abkhazia' => 'Abkhazia',
'Artsakh' => 'Artsakh',
];
}
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace App\Modules\CurrencyRate\Policies;
use App\Modules\CurrencyRate\Models\CurrencyRate;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Foundation\Auth\User as AuthUser;
class CurrencyRatePolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:CurrencyRate');
}
public function view(AuthUser $authUser, CurrencyRate $currencyRate): bool
{
return $authUser->can('View:CurrencyRate');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:CurrencyRate');
}
public function update(AuthUser $authUser, CurrencyRate $currencyRate): bool
{
return $authUser->can('Update:CurrencyRate');
}
public function delete(AuthUser $authUser, CurrencyRate $currencyRate): bool
{
return $authUser->can('Delete:CurrencyRate');
}
public function restore(AuthUser $authUser, CurrencyRate $currencyRate): bool
{
return $authUser->can('Restore:CurrencyRate');
}
public function forceDelete(AuthUser $authUser, CurrencyRate $currencyRate): bool
{
return $authUser->can('ForceDelete:CurrencyRate');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:CurrencyRate');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:CurrencyRate');
}
public function replicate(AuthUser $authUser, CurrencyRate $currencyRate): bool
{
return $authUser->can('Replicate:CurrencyRate');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:CurrencyRate');
}
}

View File

@@ -0,0 +1,5 @@
<?php
namespace App\Modules\CurrencyRate\Repositories;
class CurrencyRateRepository {}

View File

@@ -17,7 +17,7 @@ class HalkbankOnlinePaymentController extends Controller
]);
$onlinePaymentRepository = OnlinePaymentRepository::make()
->paymentProvider(new HalkbankOnlinePaymentRepository);
->setPaymentProvider(new HalkbankOnlinePaymentRepository);
$paymentStatus = $onlinePaymentRepository->checkPayment($request->string('orderId'));

View File

@@ -23,14 +23,20 @@ class HalkbankOnlinePaymentRepository implements PaymentProviderContract
protected string $returnUrl = '',
protected string $description = '',
) {
$this->username = config()->string('module.halkbank-online-payment.username');
$this->password = config()->string('module.halkbank-online-payment.password');
$this->returnUrl = config()->string('module.halkbank-online-payment.returnUrl');
$this->orderNumber = $this->generateOrderNumber();
$this->username = $username !== '' ? $username : config()->string('module.halkbank-online-payment.username');
$this->password = $password !== '' ? $password : config()->string('module.halkbank-online-payment.password');
$this->amount = $amount;
$this->orderNumber = $orderNumber !== '' ? $orderNumber : $this->generateOrderNumber();
$this->returnUrl = $returnUrl !== '' ? $returnUrl : config()->string('module.halkbank-online-payment.returnUrl');
$this->description = $description !== '' ? $description : __('Payment');
}
/**
* Send request to gatewat
* Send request to gateway
*/
public function sendRequest(): Response
{

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace App\Modules\OnlinePayment\Policies;
use App\Models\User;
use App\Modules\OnlinePayment\Models\OnlinePayment;
use Illuminate\Auth\Access\HandlesAuthorization;
class OnlinePaymentPolicy
{
use HandlesAuthorization;
public function viewAny(User $user): bool
{
return $user->can('ViewAny:OnlinePayment');
}
public function view(User $user, OnlinePayment $onlinePayment): bool
{
return $user->can('View:OnlinePayment');
}
public function create(User $user): bool
{
return $user->can('Create:OnlinePayment');
}
public function update(User $user, OnlinePayment $onlinePayment): bool
{
return $user->can('Update:OnlinePayment');
}
public function delete(User $user, OnlinePayment $onlinePayment): bool
{
return $user->can('Delete:OnlinePayment');
}
public function restore(User $user, OnlinePayment $onlinePayment): bool
{
return $user->can('Restore:OnlinePayment');
}
public function forceDelete(User $user, OnlinePayment $onlinePayment): bool
{
return $user->can('ForceDelete:OnlinePayment');
}
}

View File

@@ -8,6 +8,8 @@ use App\Modules\OnlinePayment\Models\OnlinePayment;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
class OnlinePaymentRepository
{
@@ -43,6 +45,16 @@ class OnlinePaymentRepository
*/
protected Response $response;
/**
* Online payment
*/
protected OnlinePayment $onlinePayment;
/**
* API client
*/
protected string $apiClient;
/**
* If payment is successful
*/
@@ -63,9 +75,10 @@ class OnlinePaymentRepository
*/
protected string $paymentLink;
public function __construct(?Model $relatedModel = null)
public function __construct(?Model $relatedModel = null, string $apiClient = '')
{
$this->relatedModel = $relatedModel;
$this->apiClient = $apiClient;
}
/**
@@ -94,13 +107,29 @@ class OnlinePaymentRepository
/**
* Payment provder
*/
public function paymentProvider(PaymentProviderContract $provider): self
public function setPaymentProvider(PaymentProviderContract $provider): self
{
$this->provider = $provider;
return $this;
}
/**
* Payment provider
*/
public function paymentProvider(): PaymentProviderContract
{
return $this->provider;
}
/**
* Online payment
*/
public function onlinePayment(): OnlinePayment
{
return $this->onlinePayment;
}
/**
* If payment has been successfull
*/
@@ -145,6 +174,21 @@ class OnlinePaymentRepository
$this->paymentLink = string($this->response['formUrl']);
$this->createHistory();
} else {
Config::set('logging.channels.halkbank_payment_error', [
'driver' => 'single',
'path' => storage_path('logs/halkbank_payment_error.log'),
'level' => 'debug',
]);
Log::channel('halkbank_payment_error')
->error('Payment error', [
'response' => [
'title' => 'REGISTER',
'error' => $this->response->body(),
'file_line' => __FILE__.':'.__LINE__,
],
]);
}
return $this;
@@ -173,6 +217,7 @@ class OnlinePaymentRepository
'errorUrl' => $this->provider->returnUrl(),
'username' => $this->provider->username(),
'paymentStatus' => self::PENDING,
'api_client' => $this->apiClient,
];
if ($this->relatedModel) {
@@ -180,7 +225,7 @@ class OnlinePaymentRepository
$data['online_paymantable_type'] = $this->relatedModel::class;
}
OnlinePayment::create($data);
$this->onlinePayment = OnlinePayment::create($data);
}
/**
@@ -215,16 +260,19 @@ class OnlinePaymentRepository
/** @var \App\Modules\Branch\Models\Branch */
$bankBranch = $relatedResource->branch;
if (! $bankBranch || is_null($bankBranch->billing_username) || is_null($bankBranch->billing_password)) { // @phpstan-ignore-line
return $this->paymentFailed('(BRANCH NOT FOUND)');
$username = $bankBranch->offsetExists($paymentHistory->api_client.'_username') ? $bankBranch->{$paymentHistory->api_client.'_username'} : '';
$password = $bankBranch->offsetExists($paymentHistory->api_client.'_password') ? $bankBranch->{$paymentHistory->api_client.'_password'} : '';
if ($username === '' || $password === '') {
return $this->paymentFailed('(USERNAME OR PASSWORD NOT FOUND)');
}
$this->provider->setUsername($bankBranch->billing_username);
$this->provider->setPassword($bankBranch->billing_password);
$this->provider->setUsername($username);
$this->provider->setPassword($password);
$response = $this->provider->checkPayment($orderId);
if ($response['errorCode'] == '99') {
return $this->paymentFailed('(REQUEST FAILURE)');
if ($response['errorCode'] != '0') {
return $this->paymentFailed($response['errorMessage']); // @phpstan-ignore-line
}
return $response['paymentAmountInfo']['depositedAmount'] > 0 // @phpstan-ignore-line

View File

@@ -15,6 +15,8 @@ class SmsRepository
public static function sendSMS(string|int $phone, string|int $message): mixed
{
if (app()->environment('local')) {
info('Sending SMS to '.$phone.' with message: '.$message);
return true;
}

View File

@@ -57,6 +57,6 @@ trait RoleCheckers
*/
public function isSystemUser(): bool
{
return $this->isAdmin() || $this->isOperator();
return $this->isAdmin() || $this->isOperator() || $this->isCurrencyMaintainer();
}
}

View File

@@ -83,4 +83,12 @@ trait UserAdjustments
{
return $this->hasMany(UserBranch::class);
}
/**
* Full name
*/
public function fullName(): string
{
return sprintf('%s %s', $this->first_name, $this->last_name);
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace App\Modules\VisaMasterPaymentOrder\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class VisaMasterPaymentOrderController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request): void
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): void
{
//
}
/**
* Display the specified resource.
*/
public function show(Request $request): void
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request): void
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Request $request): void
{
//
}
}

View File

@@ -0,0 +1,63 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('visa_master_payment_orders', function (Blueprint $table) {
$table->id();
$table->string('unique_id')->nullable()->unique();
$table->string('type')->nullable();
$table->string('passport_name')->nullable();
$table->string('passport_surname')->nullable();
$table->string('phone')->nullable();
$table->string('email')->nullable();
$table->string('region')->nullable();
$table->foreignId('branch_id')->nullable()->constrained('branches')->cascadeOnDelete();
$table->foreignId('user_id')->nullable()->constrained('users')->cascadeOnDelete();
$table->string('address')->nullable();
$table->json('sender_datas')->nullable();
$table->json('payment_reciever')->nullable();
$table->json('documents')->nullable();
$table->string('status')->nullable();
$table->string('notes')->nullable();
$table->string('sender_full_name')->nullable();
$table->string('sender_passport_serie')->nullable();
$table->string('sender_passport_number')->nullable();
$table->string('sender_deposit_account')->nullable();
$table->string('reciever_full_name')->nullable();
$table->string('reciever_passport_serie')->nullable();
$table->string('reciever_passport_number')->nullable();
$table->string('reciever_deposit_account')->nullable();
$table->boolean('paid')->default(false);
$table->softDeletes();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('visa_master_payment_orders');
}
};

View File

@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('visa_master_settings', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->string('display_name');
$table->text('value');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('visa_master_settings');
}
};

View File

@@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('visa_master_payment_order_items', function (Blueprint $table) {
$table->id();
$table->foreignId('visa_master_payment_order_id')->constrained('visa_master_payment_orders')->nullOnDelete();
$table->foreignId('online_payment_id')->nullable()->constrained('online_payments')->nullOnDelete();
$table->string('payer_name')->nullable();
$table->string('payer_card')->nullable();
$table->string('payment_order_number')->nullable();
$table->string('tmt_payment_amount');
$table->string('usd_payment_amount');
$table->boolean('paid')->default(false);
$table->boolean('synced_with_system')->nullable()->default(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('visa_master_payment_order_items');
}
};

View File

@@ -0,0 +1,163 @@
<?php
namespace App\Modules\VisaMasterPaymentOrder\Filament\Actions;
use App\Modules\CurrencyRate\Models\CurrencyRate;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder;
use App\Modules\VisaMasterPaymentOrder\Repositories\VisaMasterPaymentOrderRepository;
use Filament\Actions\Action;
use Filament\Forms\Components\TextInput;
use Filament\Infolists\Components\TextEntry;
use Filament\Notifications\Notification;
use Filament\Schemas\Components\Fieldset;
use Filament\Schemas\Components\Utilities\Set;
use Livewire\Component;
class PayVisaMasterPaymentAction
{
public static function make(): Action
{
return Action::make('pay_visa_master_payment')
->label(sprintf('%s %s', __('Make payment for:'), today()->translatedFormat('F')))
->icon('heroicon-o-credit-card')
->modal()
->schema(function () {
$usd_to_tmt = floatval(CurrencyRate::where('currency_from', 'USD')->where('currency_to', 'TMT')->first('value')?->value);
if (! $usd_to_tmt) {
return [];
}
$visaMasterPaymentOrderRepository = VisaMasterPaymentOrderRepository::make();
$bankFee = $visaMasterPaymentOrderRepository->bankFee();
$gbusFee = $visaMasterPaymentOrderRepository->gbusFee();
$max_value = number_format($usd_to_tmt * 250, 2);
return [
Fieldset::make(__('Tax'))
->columns([
'default' => 1,
'md' => 2,
'xl' => 3,
])
->schema([
TextEntry::make('usd_to_tmt')
->extraAttributes(['class' => 'uppercase tracking-wide font-bold text-xs'])
->label(sprintf('1 USD = %s TMT', $usd_to_tmt)),
TextEntry::make('bank_fee')
->extraAttributes(['class' => 'uppercase tracking-wide font-bold text-xs'])
->label(sprintf('Bankyň tutumy: %s TMT', $bankFee)),
TextEntry::make('gbus_fee')
->extraAttributes(['class' => 'uppercase tracking-wide font-bold text-xs'])
->label(sprintf('GBÜS tutumy: %s TMT', $gbusFee)),
]),
Fieldset::make()
->columns([
'default' => 1,
'md' => 2,
'xl' => 3,
])
->schema([
TextInput::make('payment_amount')
->label(sprintf('%s (%s)', __('Payment amount'), __('TMT')))
->required()
->numeric()
->maxValue($max_value)
->helperText(sprintf('Iň ýokary möçberi: %s TMT', $max_value))
->live()
->afterStateUpdated(function (Set $set, ?string $state) use ($usd_to_tmt, $bankFee, $gbusFee) {
if (is_null($state) || $state == 0 || $state == '') {
$set('usd_rate', '');
return;
}
$state = floatval($state);
$usd_rate = $state / $usd_to_tmt;
$total_amount = $state + $bankFee + $gbusFee;
$set('usd_rate', number_format($usd_rate, 2, '.', ''));
$set('total_amount', number_format($total_amount, 2, '.', ''));
}),
TextInput::make('usd_rate')
->label(__('USD rate'))
->suffix('USD')
->readOnly(),
TextInput::make('total_amount')
->label(__('Total'))
->suffix('TMT')
->readOnly(),
]),
];
})
->action(function (array $data, VisaMasterPaymentOrder $record, Component $livewire): void {
/** @var array{payment_amount: float, usd_rate: float, total_amount: float} */
$formData = $data;
$visaMasterPaymentOrderRepository = VisaMasterPaymentOrderRepository::make();
$today = today();
// Needs to be tested...
if ($visaMasterPaymentOrderRepository->hasBeenPaidThisMonth($record, $today)) {
Notification::make()
->title(__('This month has already been paid').'!')
->body(__('This month has already been paid'))
->danger()
->send();
return;
}
// Needs to be tested...
if ($visaMasterPaymentOrderRepository->isEndOfMonth($today)) {
Notification::make()
->title(__('Payment cannot be accepted on the last day of the month').'!')
->body(__('Payment cannot be accepted on the last day of the month'))
->danger()
->send();
return;
}
// Needs to be tested...
if (! $record->branch || ! $record->branch->billing_visa_master_username) {
Notification::make()
->title(__('Contact with the operator').'!')
->body(__('Billing information is not available in the branch! The branch cannot accept Visa/Master payments.'))
->danger()
->send();
return;
}
$total_amount = $formData['payment_amount'] + $visaMasterPaymentOrderRepository->bankFee() + $visaMasterPaymentOrderRepository->gbusFee();
$onlinePaymentRepository = $visaMasterPaymentOrderRepository->createOnlinePaymentOrder($record, $total_amount);
if ($onlinePaymentRepository->failed()) {
Notification::make()
->title(__('Payment error').'!')
->body(sprintf('%s. %s. %s.', __('Problem connecting on HALKBANK SYSTEM'), __('Error has been captured'), __('Please try again later')))
->danger()
->send();
return;
}
$visaMasterPaymentOrderRepository->createPaymentRecord($record, $onlinePaymentRepository, $formData['usd_rate']);
// Tell Livewire to open it in a new tab
$livewire->js("window.open('{$onlinePaymentRepository->paymentLink()}', '_blank')");
})
->modalSubmitActionLabel(__('Pay'));
}
}

View File

@@ -0,0 +1,131 @@
<?php
namespace App\Modules\VisaMasterPaymentOrder\Models;
use App\Models\User;
use App\Modules\Branch\Interfaces\BelongsToBranch;
use App\Modules\Branch\Models\Branch;
use App\Modules\LoanOrder\Repositories\LoanOrderRepository;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
/**
* @property int $id
* @property string $unique_id
* @property string $type
* @property string $passport_name
* @property string $passport_surname
* @property string $phone
* @property string $email
* @property string $region
* @property string $address
* @property array<string, string> $sender_datas
* @property array<string, string> $payment_reciever
* @property array<string, string> $documents
* @property string $status
* @property ?string $notes
* @property string $sender_full_name
* @property string $sender_passport_serie
* @property string $sender_passport_number
* @property string $sender_deposit_account
* @property bool $paid
* @property \Illuminate\Support\Carbon $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $deleted_at
*/
class VisaMasterPaymentOrder extends Model implements BelongsToBranch, HasMedia
{
use InteractsWithMedia;
use SoftDeletes;
protected $table = 'visa_master_payment_orders';
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'sender_datas' => 'array',
'payment_reciever' => 'array',
];
/**
* User
*
* @return BelongsTo<User, $this>
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Branch
*
* @return BelongsTo<Branch, $this>
*/
public function branch(): BelongsTo
{
return $this->belongsTo(Branch::class);
}
/**
* Payment itmes
*
* @return HasMany<VisaMasterPaymentOrderItem, $this>
*/
public function paymentItems(): HasMany
{
return $this->hasMany(VisaMasterPaymentOrderItem::class, 'visa_master_payment_order_id');
}
/**
* Get applications types
*
* @return array<string, string>
*/
public static function applicationTypes(): array
{
return [
'visa' => __('Visa'),
'master' => __('Master'),
];
}
/**
* Media collections
*/
public function registerMediaCollections(): void
{
$this->addMediaCollection('receiver_requisite')->singleFile();
$this->addMediaCollection('receiver_document_stating_he_is_studying')->singleFile();
$this->addMediaCollection('receiver_ticket')->singleFile();
$this->addMediaCollection('receiver_passport_local')->singleFile();
$this->addMediaCollection('receiver_passport_international')->singleFile();
$this->addMediaCollection('receiver_visa')->singleFile();
$this->addMediaCollection('receiver_travel_stamp_on_passport')->singleFile();
$this->addMediaCollection('receiver_document_stating_he_is_studying_2')->singleFile();
$this->addMediaCollection('sender_passport_local')->singleFile();
$this->addMediaCollection('sender_passport_international')->singleFile();
$this->addMediaCollection('sender_travel_stamp_on_passport')->singleFile();
$this->addMediaCollection('sender_proof_of_kinship')->singleFile();
$this->addMediaCollection('sender_passport_local_old')->singleFile();
$this->addMediaCollection('sender_passport_local_old_replacement')->singleFile();
}
/**
* "boot" method for model
*/
protected static function boot()
{
parent::boot();
static::created(LoanOrderRepository::created());
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace App\Modules\VisaMasterPaymentOrder\Models;
use App\Modules\OnlinePayment\Models\OnlinePayment;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Carbon;
/**
* @property int $id
* @property int $visa_master_payment_order_id
* @property int $online_payment_id
* @property string $payer_name
* @property string $payer_card
* @property string $payment_order_number
* @property string $tmt_payment_amount
* @property string $usd_payment_amount
* @property bool $paid
* @property bool $synced_with_system
* @property Carbon $created_at
* @property Carbon $updated_at
*/
class VisaMasterPaymentOrderItem extends Model
{
/**
* Table
*
* @var string
*/
protected $table = 'visa_master_payment_order_items';
/**
* Guarded attributes
*/
protected $guarded = [];
/**
* Parent order
*
* @return BelongsTo<VisaMasterPaymentOrder, $this>
*/
public function parent(): BelongsTo
{
return $this->belongsTo(VisaMasterPaymentOrder::class, 'visa_master_payment_order_id');
}
/**
* Online payment
*
* @return BelongsTo<OnlinePayment, $this>
*/
public function onlinePayment(): BelongsTo
{
return $this->belongsTo(OnlinePayment::class, 'online_payment_id');
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Modules\VisaMasterPaymentOrder\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string $name
* @property string $display_name
* @property string $value
*/
class VisaMasterSettings extends Model
{
protected $table = 'visa_master_settings';
/**
* The possible types of settings.
*
* @return array<string, string>
*/
public static function types(): array
{
return [
'payment_warning_text' => __('Warning text'),
];
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace App\Modules\VisaMasterPaymentOrder\Policies;
use App\Models\User;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrderItem;
use Illuminate\Auth\Access\HandlesAuthorization;
class VisaMasterPaymentOrderItemPolicy
{
use HandlesAuthorization;
public function viewAny(User $user): bool
{
return $user->can('ViewAny:VisaMasterPaymentOrderItem');
}
public function view(User $user, VisaMasterPaymentOrderItem $visaMasterPaymentOrderItem): bool
{
return $user->can('View:VisaMasterPaymentOrderItem');
}
public function create(User $user): bool
{
return $user->can('Create:VisaMasterPaymentOrderItem');
}
public function update(User $user, VisaMasterPaymentOrderItem $visaMasterPaymentOrderItem): bool
{
return $user->can('Update:VisaMasterPaymentOrderItem');
}
public function delete(User $user, VisaMasterPaymentOrderItem $visaMasterPaymentOrderItem): bool
{
return $user->can('Delete:VisaMasterPaymentOrderItem');
}
public function restore(User $user, VisaMasterPaymentOrderItem $visaMasterPaymentOrderItem): bool
{
return $user->can('Restore:VisaMasterPaymentOrderItem');
}
public function forceDelete(User $user, VisaMasterPaymentOrderItem $visaMasterPaymentOrderItem): bool
{
return $user->can('ForceDelete:VisaMasterPaymentOrderItem');
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace App\Modules\VisaMasterPaymentOrder\Policies;
use App\Models\User;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder;
use Illuminate\Auth\Access\HandlesAuthorization;
class VisaMasterPaymentOrderPolicy
{
use HandlesAuthorization;
public function viewAny(User $user): bool
{
return $user->can('ViewAny:VisaMasterPaymentOrder');
}
public function view(User $user, VisaMasterPaymentOrder $visaMasterPaymentOrder): bool
{
return $user->can('View:VisaMasterPaymentOrder');
}
public function create(User $user): bool
{
return $user->can('Create:VisaMasterPaymentOrder');
}
public function update(User $user, VisaMasterPaymentOrder $visaMasterPaymentOrder): bool
{
return $user->can('Update:VisaMasterPaymentOrder');
}
public function delete(User $user, VisaMasterPaymentOrder $visaMasterPaymentOrder): bool
{
return $user->can('Delete:VisaMasterPaymentOrder');
}
public function restore(User $user, VisaMasterPaymentOrder $visaMasterPaymentOrder): bool
{
return $user->can('Restore:VisaMasterPaymentOrder');
}
public function forceDelete(User $user, VisaMasterPaymentOrder $visaMasterPaymentOrder): bool
{
return $user->can('ForceDelete:VisaMasterPaymentOrder');
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace App\Modules\VisaMasterPaymentOrder\Policies;
use App\Models\User;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterSettings;
use Illuminate\Auth\Access\HandlesAuthorization;
class VisaMasterSettingsPolicy
{
use HandlesAuthorization;
public function viewAny(User $user): bool
{
return $user->can('ViewAny:VisaMasterSettings');
}
public function view(User $user, VisaMasterSettings $visaMasterSettings): bool
{
return $user->can('View:VisaMasterSettings');
}
public function create(User $user): bool
{
return $user->can('Create:VisaMasterSettings');
}
public function update(User $user, VisaMasterSettings $visaMasterSettings): bool
{
return $user->can('Update:VisaMasterSettings');
}
public function delete(User $user, VisaMasterSettings $visaMasterSettings): bool
{
return $user->can('Delete:VisaMasterSettings');
}
public function restore(User $user, VisaMasterSettings $visaMasterSettings): bool
{
return $user->can('Restore:VisaMasterSettings');
}
public function forceDelete(User $user, VisaMasterSettings $visaMasterSettings): bool
{
return $user->can('ForceDelete:VisaMasterSettings');
}
}

View File

@@ -0,0 +1,116 @@
<?php
namespace App\Modules\VisaMasterPaymentOrder\Repositories;
use App\Modules\HalkbankOnlinePayment\Repositories\HalkbankOnlinePaymentRepository;
use App\Modules\Makeable;
use App\Modules\OnlinePayment\Repositories\OnlinePaymentRepository;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder;
use App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrderItem;
use Illuminate\Support\Carbon;
class VisaMasterPaymentOrderRepository
{
use Makeable;
/**
* Bank fee
*/
public int|float $bankFee = 20;
/**
* Gbus fee
*/
public int|float $gbusFee = 3;
/**
* Bank fee
*/
public function bankFee(): int|float
{
return $this->bankFee;
}
/**
* Gbus fee
*/
public function gbusFee(): int|float
{
return $this->gbusFee;
}
/**
* Has been paid this month
*/
public function hasBeenPaidThisMonth(VisaMasterPaymentOrder $record, Carbon $today): bool
{
return $record->paymentItems->contains(
fn (VisaMasterPaymentOrderItem $item): bool => $item->paid && $item->created_at->format('m Y') === $today->format('m Y')
);
}
/**
* Is end of month
*/
public function isEndOfMonth(Carbon $today): bool
{
$lastDay = $today->copy()->endOfMonth();
$lastDayOfWeek = $lastDay->format('l');
$oneDayBefore = $lastDay->copy()->subDay(); // Saturday...
$twoDaysBefore = $lastDay->copy()->subDays(2); // Friday...
// Condition 1: Today is the last day
if ($today->isSameDay($lastDay)) {
return true;
}
// Condition 2: Last day is Sunday → forbid Friday & Saturday
if ($lastDayOfWeek === 'Sunday' && ($today->isSameDay($oneDayBefore) || $today->isSameDay($twoDaysBefore))) {
return true;
}
// Condition 3: Last day is Saturday → forbid Friday
if ($lastDayOfWeek === 'Saturday' && $today->isSameDay($oneDayBefore)) {
return true;
}
// Default: allow payment
return false;
}
/**
* Create online payment order
*/
public function createOnlinePaymentOrder(VisaMasterPaymentOrder $record, int|float|string $amount): OnlinePaymentRepository
{
/** @var \App\Modules\Branch\Models\Branch */
$branch = $record->branch;
return OnlinePaymentRepository::make(relatedModel: $record, apiClient: 'billing_visa_master')
->setPaymentProvider(
HalkbankOnlinePaymentRepository::make(
username: $branch->billing_visa_master_username,
password: $branch->billing_visa_master_password,
amount: $amount,
returnUrl: route('halkbank-online-payment.store'),
description: 'Visa/Master tölegi'
)
)
->sendRequest();
}
/**
* Create payment record
*/
public function createPaymentRecord(VisaMasterPaymentOrder $record, OnlinePaymentRepository $onlinePaymentRepository, int|string|float $usdRate): void
{
VisaMasterPaymentOrderItem::create([
'visa_master_payment_order_id' => $record->id,
'online_payment_id' => $onlinePaymentRepository->onlinePayment()->id,
'payment_order_number' => $onlinePaymentRepository->onlinePayment()->orderNumber,
'tmt_payment_amount' => $onlinePaymentRepository->paymentProvider()->amount(),
'usd_payment_amount' => $usdRate,
]);
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Modules\VisaMasterPaymentOrder;
use App\Modules\Makeable;
use App\Modules\ModuleContract;
class VisaMasterPaymentOrderModule implements ModuleContract
{
use Makeable;
/**
* Module is enabled
*/
protected bool $enabled = true;
/**
* Check if is module enabled
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Disable module
*/
public function disable(): void
{
$this->enabled = false;
}
/**
* Enable module
*/
public function enable(): void
{
$this->enabled = true;
}
/**
* Check if module has a filament resource
*/
public function hasFilamentResource(): bool
{
return false;
}
/**
* Get module composer requirements
*/
public function getComposerRequirements(): array
{
return [];
}
/**
* Get module composer suggestions
*/
public function getComposerSuggestions(): array
{
return [];
}
}

View File

@@ -3,9 +3,12 @@
namespace App\Providers;
use AbdulmajeedJamaan\FilamentTranslatableTabs\TranslatableTabs;
use BezhanSalleh\LanguageSwitch\Events\LocaleChanged;
use BezhanSalleh\LanguageSwitch\LanguageSwitch;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\ServiceProvider;
use Spatie\Translatable\HasTranslations;
@@ -33,6 +36,17 @@ class AppServiceProvider extends ServiceProvider
->locales(array_keys(baseLocales()));
});
LanguageSwitch::configureUsing(function (LanguageSwitch $switch) {
$switch
->locales(['tk', 'en', 'ru']);
});
Event::listen(function (LocaleChanged $event) {
if (array_key_exists($event->locale, baseLocales())) {
user()->update(['locale' => $event->locale]);
}
});
/**
* Order by translation for spatie/laravel-translatable
*

View File

@@ -8,6 +8,18 @@ use Illuminate\Support\ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
\App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrder::class => \App\Modules\VisaMasterPaymentOrder\Policies\VisaMasterPaymentOrderPolicy::class,
\App\Modules\VisaMasterPaymentOrder\Models\VisaMasterPaymentOrderItem::class => \App\Modules\VisaMasterPaymentOrder\Policies\VisaMasterPaymentOrderItemPolicy::class,
\App\Modules\VisaMasterPaymentOrder\Models\VisaMasterSettings::class => \App\Modules\VisaMasterPaymentOrder\Policies\VisaMasterSettingsPolicy::class,
\App\Modules\OnlinePayment\Models\OnlinePayment::class => \App\Modules\OnlinePayment\Policies\OnlinePaymentPolicy::class,
];
/**
* Register services.
*/

View File

@@ -3,10 +3,10 @@
namespace App\Providers\Filament;
use App\Http\Middleware\EnsureProfileIsFilled;
use App\Http\Middleware\EnsureUserHasRole;
use App\Livewire\UserProfileFields;
use App\Modules\BaseAuth\Middleware\RedirectIfUserPhoneIsUnVerfied;
use BezhanSalleh\FilamentShield\FilamentShieldPlugin;
use CraftForge\FilamentLanguageSwitcher\FilamentLanguageSwitcherPlugin;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\AuthenticateSession;
use Filament\Http\Middleware\DisableBladeIconComponents;
@@ -16,6 +16,7 @@ use Filament\Panel;
use Filament\PanelProvider;
use Filament\Support\Colors\Color;
use Filament\Widgets\AccountWidget;
use Filament\Widgets\FilamentInfoWidget;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
@@ -23,13 +24,12 @@ use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Joaopaulolndev\FilamentEditProfile\FilamentEditProfilePlugin;
use RalphJSmit\Filament\Upload\FilamentUpload;
class WorkPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
// #content\.form-actions > div
return $panel
->default()
->id('work')
@@ -46,6 +46,7 @@ class WorkPanelProvider extends PanelProvider
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets')
->widgets([
AccountWidget::class,
FilamentInfoWidget::class,
])
->middleware([
EncryptCookies::class,
@@ -63,25 +64,23 @@ class WorkPanelProvider extends PanelProvider
FilamentShieldPlugin::make()
->navigationGroup('Roles and permissions'),
FilamentLanguageSwitcherPlugin::make()
->locales([
['code' => 'tk', 'name' => 'Turkmen', 'flag' => 'tm'],
['code' => 'en', 'name' => 'English', 'flag' => 'us'],
['code' => 'ru', 'name' => 'Russian', 'flag' => 'ru'],
]),
FilamentEditProfilePlugin::make()
->setTitle(__('My profile'))
->setNavigationLabel(__('My profile'))
->setIcon('heroicon-o-user-circle')
->setSort(4)
->shouldShowEditProfileForm(false)
->customProfileComponents([
UserProfileFields::class,
]),
FilamentUpload::make(),
])
->authMiddleware([
Authenticate::class,
EnsureProfileIsFilled::class,
EnsureUserHasRole::class,
])
->spa()
->databaseTransactions()
@@ -92,6 +91,7 @@ class WorkPanelProvider extends PanelProvider
'primary' => Color::Indigo,
'success' => Color::Emerald,
'warning' => Color::Orange,
]);
])
->viteTheme('resources/css/filament/work/theme.css');
}
}

View File

@@ -7,6 +7,7 @@ use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)

View File

@@ -8,15 +8,21 @@
"require": {
"php": "^8.2",
"abdulmajeed-jamaan/filament-translatable-tabs": "^4.0",
"bezhansalleh/filament-language-switch": "^4.0",
"bezhansalleh/filament-shield": "^4.0",
"craft-forge/filament-language-switcher": "^1.0",
"filament/filament": "^4.0",
"filament/spatie-laravel-media-library-plugin": "^4.0",
"halaxa/json-machine": "^1.2",
"joaopaulolndev/filament-edit-profile": "^2.0",
"laravel/framework": "^12.0",
"laravel/sanctum": "^4.0",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"mpdf/mpdf": "^8.2",
"phpoffice/phpword": "dev-master",
"ralphjsmit/laravel-filament-upload": "^1.1",
"spatie/laravel-activitylog": "^4.10",
"spatie/laravel-medialibrary": "^11.17",
"spatie/laravel-translatable": "^6.11",
"stevebauman/location": "^7.6"
},
@@ -47,6 +53,12 @@
"Tests\\": "tests/"
}
},
"repositories": [
{
"type": "composer",
"url": "https://satis.ralphjsmit.com"
}
],
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
@@ -87,7 +99,8 @@
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true
"php-http/discovery": true,
"ralphjsmit/packages": true
}
},
"minimum-stability": "stable",

2789
composer.lock generated

File diff suppressed because it is too large Load Diff

52
config/activitylog.php Normal file
View File

@@ -0,0 +1,52 @@
<?php
return [
/*
* If set to false, no activities will be saved to the database.
*/
'enabled' => env('ACTIVITY_LOGGER_ENABLED', true),
/*
* When the clean-command is executed, all recording activities older than
* the number of days specified here will be deleted.
*/
'delete_records_older_than_days' => 365,
/*
* If no log name is passed to the activity() helper
* we use this default log name.
*/
'default_log_name' => 'default',
/*
* You can specify an auth driver here that gets user models.
* If this is null we'll use the current Laravel auth driver.
*/
'default_auth_driver' => null,
/*
* If set to true, the subject returns soft deleted models.
*/
'subject_returns_soft_deleted_models' => false,
/*
* This model will be used to log activity.
* It should implement the Spatie\Activitylog\Contracts\Activity interface
* and extend Illuminate\Database\Eloquent\Model.
*/
'activity_model' => \Spatie\Activitylog\Models\Activity::class,
/*
* This is the name of the table that will be created by the migration and
* used by the Activity model shipped with this package.
*/
'table_name' => env('ACTIVITY_LOGGER_TABLE_NAME', 'activity_log'),
/*
* This is the database connection that will be used by the migration and
* the Activity model shipped with this package. In case it's not set
* Laravel's database.default will be used instead.
*/
'database_connection' => env('ACTIVITY_LOGGER_DB_CONNECTION'),
];

303
config/media-library.php Normal file
View File

@@ -0,0 +1,303 @@
<?php
return [
/*
* The disk on which to store added files and derived images by default. Choose
* one or more of the disks you've configured in config/filesystems.php.
*/
'disk_name' => env('MEDIA_DISK', 'public'),
/*
* The maximum file size of an item in bytes.
* Adding a larger file will result in an exception.
*/
'max_file_size' => 1024 * 1024 * 10, // 10MB
/*
* This queue connection will be used to generate derived and responsive images.
* Leave empty to use the default queue connection.
*/
'queue_connection_name' => env('QUEUE_CONNECTION', 'sync'),
/*
* This queue will be used to generate derived and responsive images.
* Leave empty to use the default queue.
*/
'queue_name' => env('MEDIA_QUEUE', ''),
/*
* By default all conversions will be performed on a queue.
*/
'queue_conversions_by_default' => env('QUEUE_CONVERSIONS_BY_DEFAULT', true),
/*
* Should database transactions be run after database commits?
*/
'queue_conversions_after_database_commit' => env('QUEUE_CONVERSIONS_AFTER_DB_COMMIT', true),
/*
* The fully qualified class name of the media model.
*/
'media_model' => Spatie\MediaLibrary\MediaCollections\Models\Media::class,
/*
* The fully qualified class name of the media observer.
*/
'media_observer' => Spatie\MediaLibrary\MediaCollections\Models\Observers\MediaObserver::class,
/*
* When enabled, media collections will be serialised using the default
* laravel model serialization behaviour.
*
* Keep this option disabled if using Media Library Pro components (https://medialibrary.pro)
*/
'use_default_collection_serialization' => false,
/*
* The fully qualified class name of the model used for temporary uploads.
*
* This model is only used in Media Library Pro (https://medialibrary.pro)
*/
'temporary_upload_model' => Spatie\MediaLibraryPro\Models\TemporaryUpload::class,
/*
* When enabled, Media Library Pro will only process temporary uploads that were uploaded
* in the same session. You can opt to disable this for stateless usage of
* the pro components.
*/
'enable_temporary_uploads_session_affinity' => true,
/*
* When enabled, Media Library pro will generate thumbnails for uploaded file.
*/
'generate_thumbnails_for_temporary_uploads' => true,
/*
* This is the class that is responsible for naming generated files.
*/
'file_namer' => Spatie\MediaLibrary\Support\FileNamer\DefaultFileNamer::class,
/*
* The class that contains the strategy for determining a media file's path.
*/
'path_generator' => Spatie\MediaLibrary\Support\PathGenerator\DefaultPathGenerator::class,
/*
* The class that contains the strategy for determining how to remove files.
*/
'file_remover_class' => Spatie\MediaLibrary\Support\FileRemover\DefaultFileRemover::class,
/*
* Here you can specify which path generator should be used for the given class.
*/
'custom_path_generators' => [
// Model::class => PathGenerator::class
// or
// 'model_morph_alias' => PathGenerator::class
],
/*
* When urls to files get generated, this class will be called. Use the default
* if your files are stored locally above the site root or on s3.
*/
'url_generator' => Spatie\MediaLibrary\Support\UrlGenerator\DefaultUrlGenerator::class,
/*
* Moves media on updating to keep path consistent. Enable it only with a custom
* PathGenerator that uses, for example, the media UUID.
*/
'moves_media_on_update' => false,
/*
* Whether to activate versioning when urls to files get generated.
* When activated, this attaches a ?v=xx query string to the URL.
*/
'version_urls' => false,
/*
* The media library will try to optimize all converted images by removing
* metadata and applying a little bit of compression. These are
* the optimizers that will be used by default.
*/
'image_optimizers' => [
Spatie\ImageOptimizer\Optimizers\Jpegoptim::class => [
'-m85', // set maximum quality to 85%
'--force', // ensure that progressive generation is always done also if a little bigger
'--strip-all', // this strips out all text information such as comments and EXIF data
'--all-progressive', // this will make sure the resulting image is a progressive one
],
Spatie\ImageOptimizer\Optimizers\Pngquant::class => [
'--force', // required parameter for this package
],
Spatie\ImageOptimizer\Optimizers\Optipng::class => [
'-i0', // this will result in a non-interlaced, progressive scanned image
'-o2', // this set the optimization level to two (multiple IDAT compression trials)
'-quiet', // required parameter for this package
],
Spatie\ImageOptimizer\Optimizers\Svgo::class => [
'--disable=cleanupIDs', // disabling because it is known to cause troubles
],
Spatie\ImageOptimizer\Optimizers\Gifsicle::class => [
'-b', // required parameter for this package
'-O3', // this produces the slowest but best results
],
Spatie\ImageOptimizer\Optimizers\Cwebp::class => [
'-m 6', // for the slowest compression method in order to get the best compression.
'-pass 10', // for maximizing the amount of analysis pass.
'-mt', // multithreading for some speed improvements.
'-q 90', // quality factor that brings the least noticeable changes.
],
Spatie\ImageOptimizer\Optimizers\Avifenc::class => [
'-a cq-level=23', // constant quality level, lower values mean better quality and greater file size (0-63).
'-j all', // number of jobs (worker threads, "all" uses all available cores).
'--min 0', // min quantizer for color (0-63).
'--max 63', // max quantizer for color (0-63).
'--minalpha 0', // min quantizer for alpha (0-63).
'--maxalpha 63', // max quantizer for alpha (0-63).
'-a end-usage=q', // rate control mode set to Constant Quality mode.
'-a tune=ssim', // SSIM as tune the encoder for distortion metric.
],
],
/*
* These generators will be used to create an image of media files.
*/
'image_generators' => [
Spatie\MediaLibrary\Conversions\ImageGenerators\Image::class,
Spatie\MediaLibrary\Conversions\ImageGenerators\Webp::class,
Spatie\MediaLibrary\Conversions\ImageGenerators\Avif::class,
Spatie\MediaLibrary\Conversions\ImageGenerators\Pdf::class,
Spatie\MediaLibrary\Conversions\ImageGenerators\Svg::class,
Spatie\MediaLibrary\Conversions\ImageGenerators\Video::class,
],
/*
* The path where to store temporary files while performing image conversions.
* If set to null, storage_path('media-library/temp') will be used.
*/
'temporary_directory_path' => null,
/*
* The engine that should perform the image conversions.
* Should be either `gd` or `imagick`.
*/
'image_driver' => env('IMAGE_DRIVER', 'gd'),
/*
* FFMPEG & FFProbe binaries paths, only used if you try to generate video
* thumbnails and have installed the php-ffmpeg/php-ffmpeg composer
* dependency.
*/
'ffmpeg_path' => env('FFMPEG_PATH', '/usr/bin/ffmpeg'),
'ffprobe_path' => env('FFPROBE_PATH', '/usr/bin/ffprobe'),
/*
* The timeout (in seconds) that will be used when generating video
* thumbnails via FFMPEG.
*/
'ffmpeg_timeout' => env('FFMPEG_TIMEOUT', 900),
/*
* The number of threads that FFMPEG should use. 0 means that FFMPEG
* may decide itself.
*/
'ffmpeg_threads' => env('FFMPEG_THREADS', 0),
/*
* Here you can override the class names of the jobs used by this package. Make sure
* your custom jobs extend the ones provided by the package.
*/
'jobs' => [
'perform_conversions' => Spatie\MediaLibrary\Conversions\Jobs\PerformConversionsJob::class,
'generate_responsive_images' => Spatie\MediaLibrary\ResponsiveImages\Jobs\GenerateResponsiveImagesJob::class,
],
/*
* When using the addMediaFromUrl method you may want to replace the default downloader.
* This is particularly useful when the url of the image is behind a firewall and
* need to add additional flags, possibly using curl.
*/
'media_downloader' => Spatie\MediaLibrary\Downloaders\DefaultDownloader::class,
/*
* When using the addMediaFromUrl method the SSL is verified by default.
* This is option disables SSL verification when downloading remote media.
* Please note that this is a security risk and should only be false in a local environment.
*/
'media_downloader_ssl' => env('MEDIA_DOWNLOADER_SSL', true),
/*
* The default lifetime in minutes for temporary urls.
* This is used when you call the `getLastTemporaryUrl` or `getLastTemporaryUrl` method on a media item.
*/
'temporary_url_default_lifetime' => env('MEDIA_TEMPORARY_URL_DEFAULT_LIFETIME', 5),
'remote' => [
/*
* Any extra headers that should be included when uploading media to
* a remote disk. Even though supported headers may vary between
* different drivers, a sensible default has been provided.
*
* Supported by S3: CacheControl, Expires, StorageClass,
* ServerSideEncryption, Metadata, ACL, ContentEncoding
*/
'extra_headers' => [
'CacheControl' => 'max-age=604800',
],
],
'responsive_images' => [
/*
* This class is responsible for calculating the target widths of the responsive
* images. By default we optimize for filesize and create variations that each are 30%
* smaller than the previous one. More info in the documentation.
*
* https://docs.spatie.be/laravel-medialibrary/v9/advanced-usage/generating-responsive-images
*/
'width_calculator' => Spatie\MediaLibrary\ResponsiveImages\WidthCalculator\FileSizeOptimizedWidthCalculator::class,
/*
* By default rendering media to a responsive image will add some javascript and a tiny placeholder.
* This ensures that the browser can already determine the correct layout.
* When disabled, no tiny placeholder is generated.
*/
'use_tiny_placeholders' => true,
/*
* This class will generate the tiny placeholder used for progressive image loading. By default
* the media library will use a tiny blurred jpg image.
*/
'tiny_placeholder_generator' => Spatie\MediaLibrary\ResponsiveImages\TinyPlaceholderGenerator\Blurred::class,
],
/*
* When enabling this option, a route will be registered that will enable
* the Media Library Pro Vue and React components to move uploaded files
* in a S3 bucket to their right place.
*/
'enable_vapor_uploads' => env('ENABLE_MEDIA_LIBRARY_VAPOR_UPLOADS', false),
/*
* When converting Media instances to response the media library will add
* a `loading` attribute to the `img` tag. Here you can set the default
* value of that attribute.
*
* Possible values: 'lazy', 'eager', 'auto' or null if you don't want to set any loading instruction.
*
* More info: https://css-tricks.com/native-lazy-loading/
*/
'default_loading_attribute_value' => null,
/*
* You can specify a prefix for that is used for storing all media.
* If you set this to `/my-subdir`, all your media will be stored in a `/my-subdir` directory.
*/
'prefix' => env('MEDIA_PREFIX', ''),
/*
* When forcing lazy loading, media will be loaded even if you don't eager load media and you have
* disabled lazy loading globally in the service provider.
*/
'force_lazy_loading' => env('FORCE_MEDIA_LIBRARY_LAZY_LOADING', true),
];

84
config/sanctum.php Normal file
View File

@@ -0,0 +1,84 @@
<?php
use Laravel\Sanctum\Sanctum;
return [
/*
|--------------------------------------------------------------------------
| Stateful Domains
|--------------------------------------------------------------------------
|
| Requests from the following domains / hosts will receive stateful API
| authentication cookies. Typically, these should include your local
| and production domains which access your API via a frontend SPA.
|
*/
'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::currentRequestHost(),
))),
/*
|--------------------------------------------------------------------------
| Sanctum Guards
|--------------------------------------------------------------------------
|
| This array contains the authentication guards that will be checked when
| Sanctum is trying to authenticate a request. If none of these guards
| are able to authenticate the request, Sanctum will use the bearer
| token that's present on an incoming request for authentication.
|
*/
'guard' => ['web'],
/*
|--------------------------------------------------------------------------
| Expiration Minutes
|--------------------------------------------------------------------------
|
| This value controls the number of minutes until an issued token will be
| considered expired. This will override any values set in the token's
| "expires_at" attribute, but first-party sessions are not affected.
|
*/
'expiration' => null,
/*
|--------------------------------------------------------------------------
| Token Prefix
|--------------------------------------------------------------------------
|
| Sanctum can prefix new tokens in order to take advantage of numerous
| security scanning initiatives maintained by open source platforms
| that notify developers if they commit tokens into repositories.
|
| See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning
|
*/
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),
/*
|--------------------------------------------------------------------------
| Sanctum Middleware
|--------------------------------------------------------------------------
|
| When authenticating your first-party SPA with Sanctum you may need to
| customize some of the middleware Sanctum uses while processing the
| request. You may change the middleware listed below as required.
|
*/
'middleware' => [
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class,
'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
],
];

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
if (! Schema::hasTable('media')) {
Schema::create('media', function (Blueprint $table) {
$table->id();
$table->morphs('model');
$table->uuid()->nullable()->unique();
$table->string('collection_name');
$table->string('name');
$table->string('file_name');
$table->string('mime_type')->nullable();
$table->string('disk');
$table->string('conversions_disk')->nullable();
$table->unsignedBigInteger('size');
$table->json('manipulations');
$table->json('custom_properties');
$table->json('generated_conversions');
$table->json('responsive_images');
$table->unsignedInteger('order_column')->nullable()->index();
$table->nullableTimestamps();
});
}
}
};

View File

@@ -0,0 +1,47 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('action_events', function (Blueprint $table) {
$table->id();
$table->string('batch_id')->nullable();
$table->unsignedBigInteger('user_id')->nullable()->index();
$table->string('name')->index();
$table->string('actionable_type')->nullable()->index();
$table->unsignedBigInteger('actionable_id')->nullable()->index();
$table->string('target_type')->nullable()->index();
$table->unsignedBigInteger('target_id')->nullable()->index();
$table->string('model_type')->nullable()->index();
$table->unsignedBigInteger('model_id')->nullable()->index();
$table->text('fields')->nullable();
$table->string('status')->index()->default('finished');
$table->text('exception')->nullable();
$table->json('original')->nullable();
$table->json('changes')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('action_events');
}
};

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('personal_access_tokens', function (Blueprint $table) {
$table->id();
$table->morphs('tokenable');
$table->text('name');
$table->string('token', 64)->unique();
$table->text('abilities')->nullable();
$table->timestamp('last_used_at')->nullable();
$table->timestamp('expires_at')->nullable()->index();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('personal_access_tokens');
}
};

View File

@@ -13,9 +13,7 @@ class DatabaseSeeder extends Seeder
public function run(): void
{
$this->call([
// UsersTableSeeder::class,
// ShieldSeeder::class,
// FillJsonData::class,
ShieldSeeder::class,
]);
}
}

View File

@@ -9,8 +9,78 @@ class FillJsonData extends Seeder
/**
* Run the database seeds.
*/
public function run(): void
public function run(): void
{
// provinces
}
protected function seedUsers(): void
{
(new Migrators\UsersMigrator)->migrate();
}
protected function seedLoanTypes(): void
{
(new Migrators\LoanTypesMigrator)->migrate();
}
protected function seedProvinces(): void
{
(new Migrators\ProvincesMigrator)->migrate();
}
protected function seedBranches(): void
{
(new Migrators\BranchesMigrator)->migrate();
}
protected function loanOrderRequiredDocs(): void
{
(new Migrators\LoanOrderRequiredDocsMigrator)->migrate();
}
protected function seedLoanOrders(): void
{
(new Migrators\LoanOrdersMigrator)->migrate();
}
protected function seedCardStates(): void
{
(new Migrators\CardStatesMigrator)->migrate();
}
protected function seedCardTypes(): void
{
(new Migrators\CardTypesMigrator)->migrate();
}
protected function cardOrders(): void
{
(new Migrators\CardOrdersMigrator)->migrate();
}
protected function cardPinOrders(): void
{
(new Migrators\CardPinOrdersMigrator)->migrate();
}
protected function seedVisaMasterPaymentOrders(): void
{
(new Migrators\VisaMasterPaymentOrdersMigrator)->migrate();
}
protected function seedActionEvents(): void
{
(new Migrators\ActionEventsMigrator)->migrate();
}
protected function seedBranchUser(): void
{
(new Migrators\BranchUserMigrator)->migrate();
}
protected function seedCurrencyRates(): void
{
(new Migrators\CurrencyRatesMigrator)->migrate();
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Database\Seeders\Migrators;
use Illuminate\Support\Facades\DB;
use JsonMachine\Items;
class ActionEventsMigrator
{
public function migrate(): void
{
DB::table('action_events')->truncate();
$path = database_path('data/tested/action_events.json');
$items = Items::fromFile($path);
foreach ($items as $id => $item) {
if (! $item) {
continue;
}
DB::table('action_events')->insert((array) $item);
}
DB::statement("SELECT setval('action_events_id_seq', (SELECT MAX(id) from action_events));");
DB::statement("SELECT nextval('action_events_id_seq');");
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Database\Seeders\Migrators;
use Illuminate\Support\Facades\DB;
use JsonMachine\Items;
class BranchUserMigrator
{
public function migrate(): void
{
DB::table('branch_user')->truncate();
$path = database_path('data/tested/branch_user.json');
$items = Items::fromFile($path);
foreach ($items as $id => $item) {
if (! $item) {
continue;
}
DB::table('branch_user')->insert((array) $item);
DB::statement("SELECT setval('branch_user_id_seq', (SELECT MAX(id) from branch_user));");
DB::statement("SELECT nextval('branch_user_id_seq');");
}
}
}

Some files were not shown because too many files have changed in this diff Show More