13 Commits

Author SHA1 Message Date
7bbe6e2d2a Merge pull request #5 from kingjaypee12/jp/fix-sales-breadcrumbs
feat(client): add client context to sales and expenses creation
2026-02-15 17:44:32 +08:00
Jp
fbc01bf1a4 feat(client): add client context to sales and expenses creation
- Disable branch selection in sale form when creating from client context
- Pass client ID to create pages via URL parameter
- Update breadcrumbs to reflect client navigation path
- Simplify relation managers by reusing resource tables and adding custom create actions
2026-02-15 17:43:33 +08:00
Jp
ff07f6f810 fix: remove debug statement from account options query
The dd() statement was accidentally left in production code, causing debug output to be displayed. This removes it to ensure proper functionality.
2026-02-14 14:14:33 +08:00
Jp
2bd8e99f64 fix: remove branch filter and add debug output for revenue accounts
The branch filter was incorrectly limiting available revenue accounts in certain scenarios. Temporarily added debug output to investigate account query results.
2026-02-14 14:13:24 +08:00
Jp
950e5613e6 Merge https://git.jpaleviado.site/kingjaypee12/MKM 2026-02-12 16:30:41 +08:00
Jp
fc118b8a6c Merge branch 'main' of https://github.com/kingjaypee12/MKM-App 2026-02-12 16:29:36 +08:00
Jp
a80e9a7b1c feat: add Laravel Horizon for queue monitoring and management
- Install Horizon package and configure with default settings
- Add HorizonServiceProvider with access gate for super_admin role only
- Integrate Horizon dashboard link into Filament admin panel navigation
- Update composer dependencies including Horizon and related package updates
2026-02-12 16:29:30 +08:00
d793abec9e Merge pull request #4 from kingjaypee12/fix/error-on-production
feat(TransmittalResource): enhance search functionality across relate…
2026-02-12 16:29:16 +08:00
fcac27b34d Merge pull request 'feat(TransmittalResource): enhance search functionality across related models' (#5) from fix/error-on-production into main
Reviewed-on: #5
2026-02-12 08:15:26 +00:00
22ea384d52 Merge pull request #3 from kingjaypee12/fix/error-on-production
Fix/error on production
2026-02-11 05:52:54 +08:00
7174bd6c90 Merge pull request 'feat: implement FilamentUser interface for User model' (#4) from fix/error-on-production into main
Reviewed-on: #4
2026-02-10 21:45:13 +00:00
4f9ec9ebfb Merge pull request 'fix: force HTTPS scheme in all environments' (#3) from fix/error-on-production into main
Reviewed-on: #3
2026-02-10 21:42:11 +00:00
f63be7fa5e Merge pull request 'fix: enforce HTTPS in production environment' (#2) from fix/error-on-production into main
Reviewed-on: #2
2026-02-10 21:38:32 +00:00
10 changed files with 407 additions and 77 deletions

View File

@@ -24,31 +24,13 @@ class ExpensesRelationManager extends RelationManager
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('supplier')
->columns([
TextColumn::make('supplier'),
TextColumn::make('reference_number'),
TextColumn::make('voucher_number'),
TextColumn::make('branch.code'),
TextColumn::make('happened_on'),
])
->filters([
//
])
->headerActions([
Tables\Actions\CreateAction::make()
->url(fn () => ExpenseResource::getUrl('create', ['client_id' => $this->getOwnerRecord()->id])),
])
->actions([
Tables\Actions\EditAction::make()
->url(fn (Expense $record) => ExpenseResource::getUrl('edit', ['record' => $record])),
Tables\Actions\DeleteAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
return ExpenseResource::table($table)->headerActions([
Tables\Actions\Action::make('New Expense')->action('openCreateForm'),
]);
}
public function openCreateForm()
{
return redirect()->route('filament.admin.resources.expenses.create', ['client_id' => $this->getOwnerRecord()->id]);
}
}

View File

@@ -24,36 +24,13 @@ class SalesRelationManager extends RelationManager
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('title')
->columns([
TextColumn::make('id')->label('ID')->sortable(),
TextColumn::make('branch.code')->label('Branch')->sortable(),
TextColumn::make('happened_on')->label('Date')->date()->sortable(),
TextColumn::make('gross_amount')->label('Gross Amount')->numeric()->sortable(),
TextColumn::make('exempt')->label('Exempt')->numeric()->sortable(),
TextColumn::make('vatable_amount')->label('Vatable Amount')->numeric()->sortable(),
TextColumn::make('output_tax')->label('Output Tax')->numeric()->sortable(),
TextColumn::make('payable_withholding_tax')->label('Payable Withholding Tax')->numeric()->sortable(),
TextColumn::make('discount')->label('Discount')->numeric()->sortable(),
TextColumn::make('net_amount')->label('Net Amount')->numeric()->sortable(),
])
->filters([
//
])
->headerActions([
Tables\Actions\CreateAction::make()
->url(fn () => SaleResource::getUrl('create', ['client_id' => $this->getOwnerRecord()->id])),
])
->actions([
Tables\Actions\EditAction::make()
->url(fn (Sale $record) => SaleResource::getUrl('edit', ['record' => $record])),
Tables\Actions\DeleteAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
return SaleResource::table($table)->headerActions([
Tables\Actions\Action::make('New Sale')->action('openCreateForm'),
]);
}
public function openCreateForm()
{
return redirect()->route('filament.admin.resources.sales.create', ['client_id' => $this->getOwnerRecord()->id]);
}
}

View File

@@ -4,7 +4,9 @@ namespace App\Filament\Resources\ExpenseResource\Pages;
use App\Actions\Transactions\CreateTransactionAction;
use App\DataObjects\CreateTransactionDTO;
use App\Filament\Resources\ClientResource;
use App\Filament\Resources\ExpenseResource;
use App\Models\Client;
use Exception;
use Filament\Resources\Pages\CreateRecord;
use Illuminate\Support\Arr;
@@ -15,6 +17,39 @@ class CreateExpense extends CreateRecord
{
protected static string $resource = ExpenseResource::class;
public ?int $clientId = null;
public function mount(): void
{
parent::mount();
$this->clientId = request()->integer('client_id');
}
public function getBreadcrumbs(): array
{
$client = $this->getClient();
if (! $client) {
return parent::getBreadcrumbs();
}
return [
ClientResource::getUrl('view', ['record' => $client->id]) => $client->company,
ClientResource::getUrl('view', ['record' => $client->id]).'?activeRelationManager=4' => 'Expenses',
$this->getResource()::getUrl('create', ['client_id' => $client->id]) => 'Create',
];
}
protected function getClient(): Client|null
{
if (! $this->clientId) {
return null;
}
return Client::find($this->clientId);
}
protected function mutateFormDataBeforeCreate(array $data): array
{

View File

@@ -40,6 +40,7 @@ class SaleResource extends Resource
->afterStateUpdated(function ($set, $get) {
$set('branch_id', '');
})
->disabled()
->required()
->live(),
Select::make('branch_id')
@@ -170,11 +171,11 @@ class SaleResource extends Resource
'client_id' => $get('../../client'),
]);
if ($get('../../branch_id')) {
$query->whereHas('balances', function ($query) use ($get) {
return $query->where('branch_id', $get('../../branch_id'));
});
}
// if ($get('../../branch_id')) {
// $query->whereHas('balances', function ($query) use ($get) {
// return $query->where('branch_id', $get('../../branch_id'));
// });
// }
$query->whereHas('accountType', function ($query) {
return $query->where('type', 'Revenue');

View File

@@ -4,8 +4,10 @@ namespace App\Filament\Resources\SaleResource\Pages;
use App\Actions\Transactions\CreateTransactionAction;
use App\DataObjects\CreateTransactionDTO;
use App\Filament\Resources\ClientResource;
use App\Filament\Resources\SaleResource;
use App\Models\Branch;
use App\Models\Client;
use App\Models\Sale;
use Filament\Resources\Pages\CreateRecord;
use Illuminate\Database\Eloquent\Model;
@@ -17,6 +19,39 @@ class CreateSale extends CreateRecord
{
protected static string $resource = SaleResource::class;
public ?int $clientId = null;
public function mount(): void
{
parent::mount();
$this->clientId = request()->integer('client_id');
}
public function getBreadcrumbs(): array
{
$client = $this->getClient();
if (! $client) {
return parent::getBreadcrumbs();
}
return [
ClientResource::getUrl('view', ['record' => $client->id]) => $client->company,
ClientResource::getUrl('view', ['record' => $client->id]).'?activeRelationManager=3' => 'Sales',
$this->getResource()::getUrl('create', ['client_id' => $client->id]) => 'Create',
];
}
protected function getClient(): Client|null
{
if (! $this->clientId) {
return null;
}
return Client::find($this->clientId);
}
protected function mutateFormDataBeforeCreate(array $data): array
{
return $this->getFormDataMutation($data);

View File

@@ -3,6 +3,7 @@
namespace App\Providers\Filament;
use BezhanSalleh\FilamentShield\FilamentShieldPlugin;
use Filament\Navigation\NavigationItem;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
@@ -39,6 +40,13 @@ class AdminPanelProvider extends PanelProvider
->pages([
Pages\Dashboard::class,
])
->navigationItems([
NavigationItem::make('Horizon')
->url(fn (): string => url(config('horizon.path')))
->icon('heroicon-o-queue-list')
->group('System')
->visible(fn (): bool => auth()->user()?->hasRole('super_admin') ?? false),
])
->databaseNotifications()
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->widgets([

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Laravel\Horizon\Horizon;
use Laravel\Horizon\HorizonApplicationServiceProvider;
class HorizonServiceProvider extends HorizonApplicationServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
parent::boot();
// Horizon::routeSmsNotificationsTo('15556667777');
// Horizon::routeMailNotificationsTo('example@example.com');
// Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
}
/**
* Register the Horizon gate.
*
* This gate determines who can access Horizon in non-local environments.
*/
protected function gate(): void
{
Gate::define('viewHorizon', function ($user) {
return $user->hasRole('super_admin');
});
}
}

View File

@@ -13,6 +13,7 @@
"bezhansalleh/filament-shield": "^3.2",
"filament/filament": "^3.2",
"laravel/framework": "^11.9",
"laravel/horizon": "^5.44",
"laravel/tinker": "^2.9",
"livewire/livewire": "^3.4",
"livewire/volt": "^1.0",

109
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "68d9041cb27c73080bced01f909a4567",
"content-hash": "60653f82a7ab603cb6e06b89358e7eec",
"packages": [
{
"name": "anourvalar/eloquent-serialize",
@@ -386,16 +386,16 @@
},
{
"name": "brick/math",
"version": "0.14.7",
"version": "0.14.8",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
"reference": "07ff363b16ef8aca9692bba3be9e73fe63f34e50"
"reference": "63422359a44b7f06cae63c3b429b59e8efcc0629"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/07ff363b16ef8aca9692bba3be9e73fe63f34e50",
"reference": "07ff363b16ef8aca9692bba3be9e73fe63f34e50",
"url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629",
"reference": "63422359a44b7f06cae63c3b429b59e8efcc0629",
"shasum": ""
},
"require": {
@@ -434,7 +434,7 @@
],
"support": {
"issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.14.7"
"source": "https://github.com/brick/math/tree/0.14.8"
},
"funding": [
{
@@ -442,7 +442,7 @@
"type": "github"
}
],
"time": "2026-02-07T10:57:35+00:00"
"time": "2026-02-10T14:33:43+00:00"
},
{
"name": "carbonphp/carbon-doctrine-types",
@@ -2617,17 +2617,96 @@
"time": "2026-01-20T15:26:20+00:00"
},
{
"name": "laravel/prompts",
"version": "v0.3.12",
"name": "laravel/horizon",
"version": "v5.44.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/prompts.git",
"reference": "4861ded9003b7f8a158176a0b7666f74ee761be8"
"url": "https://github.com/laravel/horizon.git",
"reference": "00c21e4e768112cce3f4fe576d75956dfc423de2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/prompts/zipball/4861ded9003b7f8a158176a0b7666f74ee761be8",
"reference": "4861ded9003b7f8a158176a0b7666f74ee761be8",
"url": "https://api.github.com/repos/laravel/horizon/zipball/00c21e4e768112cce3f4fe576d75956dfc423de2",
"reference": "00c21e4e768112cce3f4fe576d75956dfc423de2",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-pcntl": "*",
"ext-posix": "*",
"illuminate/contracts": "^9.21|^10.0|^11.0|^12.0|^13.0",
"illuminate/queue": "^9.21|^10.0|^11.0|^12.0|^13.0",
"illuminate/support": "^9.21|^10.0|^11.0|^12.0|^13.0",
"nesbot/carbon": "^2.17|^3.0",
"php": "^8.0",
"ramsey/uuid": "^4.0",
"symfony/console": "^6.0|^7.0|^8.0",
"symfony/error-handler": "^6.0|^7.0|^8.0",
"symfony/polyfill-php83": "^1.28",
"symfony/process": "^6.0|^7.0|^8.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^7.55|^8.36|^9.15|^10.8|^11.0",
"phpstan/phpstan": "^1.10|^2.0",
"predis/predis": "^1.1|^2.0|^3.0"
},
"suggest": {
"ext-redis": "Required to use the Redis PHP driver.",
"predis/predis": "Required when not using the Redis PHP driver (^1.1|^2.0|^3.0)."
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"Horizon": "Laravel\\Horizon\\Horizon"
},
"providers": [
"Laravel\\Horizon\\HorizonServiceProvider"
]
},
"branch-alias": {
"dev-master": "6.x-dev"
}
},
"autoload": {
"psr-4": {
"Laravel\\Horizon\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Dashboard and code-driven configuration for Laravel queues.",
"keywords": [
"laravel",
"queue"
],
"support": {
"issues": "https://github.com/laravel/horizon/issues",
"source": "https://github.com/laravel/horizon/tree/v5.44.0"
},
"time": "2026-02-10T18:18:08+00:00"
},
{
"name": "laravel/prompts",
"version": "v0.3.13",
"source": {
"type": "git",
"url": "https://github.com/laravel/prompts.git",
"reference": "ed8c466571b37e977532fb2fd3c272c784d7050d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/prompts/zipball/ed8c466571b37e977532fb2fd3c272c784d7050d",
"reference": "ed8c466571b37e977532fb2fd3c272c784d7050d",
"shasum": ""
},
"require": {
@@ -2671,9 +2750,9 @@
"description": "Add beautiful and user-friendly forms to your command-line applications.",
"support": {
"issues": "https://github.com/laravel/prompts/issues",
"source": "https://github.com/laravel/prompts/tree/v0.3.12"
"source": "https://github.com/laravel/prompts/tree/v0.3.13"
},
"time": "2026-02-03T06:57:26+00:00"
"time": "2026-02-06T12:17:10+00:00"
},
{
"name": "laravel/serializable-closure",

178
config/horizon.php Normal file
View File

@@ -0,0 +1,178 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Horizon Domain
|--------------------------------------------------------------------------
|
| This value is the prefix that will be used by Horizon to generate its
| routes. You are free to change this value to anything you like,
| such as "admin/horizon" or anything else you'd like it to be.
|
*/
'domain' => env('HORIZON_DOMAIN'),
/*
|--------------------------------------------------------------------------
| Horizon Path
|--------------------------------------------------------------------------
|
| This is the URI path where Horizon will be accessible from. Feel free
| to change this path to anything you like. Note that the path will be
| applied after the "domain" value defined above.
|
*/
'path' => env('HORIZON_PATH', 'horizon'),
/*
|--------------------------------------------------------------------------
| Horizon Redis Connection
|--------------------------------------------------------------------------
|
| This is the name of the Redis connection where Horizon will store the
| information about your jobs. This connection should be defined in
| your "database" configuration file.
|
*/
'use' => 'default',
/*
|--------------------------------------------------------------------------
| Horizon Redis Prefix
|--------------------------------------------------------------------------
|
| This prefix will be used when storing all Horizon data in Redis. You
| may modify the prefix if you are running multiple Horizon instances
| on the same server to avoid any collision between instances.
|
*/
'prefix' => env(
'HORIZON_PREFIX',
Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:'
),
/*
|--------------------------------------------------------------------------
| Horizon Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will get attached onto each Horizon route, giving you
| the chance to add your own middleware to this list or change any of
| the existing middleware. Or, you can simply stick with this list.
|
*/
'middleware' => ['web', 'auth'],
/*
|--------------------------------------------------------------------------
| Queue Wait Time Thresholds
|--------------------------------------------------------------------------
|
| This option allows you to configure when the queue "wait time" is
| considered "long", helping you identify which queues are being
| backed up and need more workers to handle the incoming load.
|
*/
'waits' => [
'redis:default' => 60,
],
/*
|--------------------------------------------------------------------------
| Job Trimming Times
|--------------------------------------------------------------------------
|
| These options determine how many minutes Horizon will keep different
| types of jobs in its database. This helps keep your database small
| and fast, while still giving you some history for debugging.
|
*/
'trim' => [
'recent' => 60,
'recent_failed' => 10080,
'failed' => 10080,
'monitored' => 10080,
],
/*
|--------------------------------------------------------------------------
| Metrics Configuration
|--------------------------------------------------------------------------
|
| Here you may configure how many minutes Horizon will keep metrics.
| These metrics include things like throughput and average wait time
| for each of your queues, giving you insight into your performance.
|
*/
'metrics' => [
'trim_snapshots' => [
'job' => 24,
'queue' => 24,
],
],
/*
|--------------------------------------------------------------------------
| Fast Termination
|--------------------------------------------------------------------------
|
| When this option is enabled, Horizon's "terminate" command will not
| wait for all jobs to finish before terminating the workers. This
| can be useful when you need to quickly restart your workers.
|
*/
'fast_termination' => false,
/*
|--------------------------------------------------------------------------
| Memory Limit (MB)
|--------------------------------------------------------------------------
|
| This value describes the maximum amount of memory the Horizon master
| process may consume before it is terminated and restarted. This
| helps prevent any memory leaks from affecting the entire server.
|
*/
'memory_limit' => 64,
/*
|--------------------------------------------------------------------------
| Queue Worker Configuration
|--------------------------------------------------------------------------
|
| Here you may define the queue worker settings used by your application
| in all environments. These settings determine how many workers will
| be used for each environment's respective queues.
|
*/
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
],
];