feat: add discount management and PDF export for transmittals
- Create Discount model, migration, and Filament resource with relation to Client - Add PDF export functionality for transmittals using DomPDF - Include discount type selection in sales transactions - Fix account filtering logic in expense resource - Update export job to generate PDF instead of Excel
This commit is contained in:
@@ -10,6 +10,7 @@ use App\Filament\Resources\ClientResource\Pages\GeneralLedger;
|
||||
use App\Filament\Resources\ClientResource\Pages\TrialBalance;
|
||||
use App\Filament\Resources\ClientResource\RelationManagers\AccountsRelationManager;
|
||||
use App\Filament\Resources\ClientResource\RelationManagers\BranchesRelationManager;
|
||||
use App\Filament\Resources\ClientResource\RelationManagers\DiscountRelationManager;
|
||||
use App\Filament\Resources\ClientResource\RelationManagers\ExpensesRelationManager;
|
||||
use App\Filament\Resources\ClientResource\RelationManagers\JournalsRelationManager;
|
||||
use App\Filament\Resources\ClientResource\RelationManagers\SalesRelationManager;
|
||||
@@ -109,6 +110,7 @@ class ClientResource extends Resource
|
||||
SalesRelationManager::class,
|
||||
ExpensesRelationManager::class,
|
||||
JournalsRelationManager::class,
|
||||
DiscountRelationManager::class,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\ClientResource\RelationManagers;
|
||||
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
|
||||
class DiscountRelationManager extends RelationManager
|
||||
{
|
||||
protected static string $relationship = 'discounts';
|
||||
|
||||
public function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('discount')
|
||||
->required()
|
||||
->maxLength(255),
|
||||
]);
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->recordTitleAttribute('discount')
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('discount'),
|
||||
])
|
||||
->filters([
|
||||
//
|
||||
])
|
||||
->headerActions([
|
||||
Tables\Actions\CreateAction::make(),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
Tables\Actions\DeleteAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
73
app/Filament/Resources/DiscountResource.php
Normal file
73
app/Filament/Resources/DiscountResource.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Resources\DiscountResource\Pages;
|
||||
use App\Filament\Resources\DiscountResource\RelationManagers;
|
||||
use App\Models\Discount;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
|
||||
class DiscountResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Discount::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
||||
|
||||
protected static bool $shouldRegisterNavigation = false;
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('discount')
|
||||
->label('Discount')
|
||||
->required(),
|
||||
Forms\Components\Hidden::make('client_id')
|
||||
->default(fn () => request()->client_id),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('discount')
|
||||
->label('Discount')
|
||||
->searchable(),
|
||||
])
|
||||
->filters([
|
||||
//
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkActionGroup::make([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListDiscounts::route('/'),
|
||||
'create' => Pages\CreateDiscount::route('/create'),
|
||||
'edit' => Pages\EditDiscount::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\DiscountResource\Pages;
|
||||
|
||||
use App\Filament\Resources\DiscountResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateDiscount extends CreateRecord
|
||||
{
|
||||
protected static string $resource = DiscountResource::class;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\DiscountResource\Pages;
|
||||
|
||||
use App\Filament\Resources\DiscountResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditDiscount extends EditRecord
|
||||
{
|
||||
protected static string $resource = DiscountResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\DiscountResource\Pages;
|
||||
|
||||
use App\Filament\Resources\DiscountResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListDiscounts extends ListRecords
|
||||
{
|
||||
protected static string $resource = DiscountResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -195,11 +195,11 @@ class ExpenseResource 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', 'Expenses');
|
||||
@@ -208,7 +208,6 @@ class ExpenseResource extends Resource
|
||||
return $query->get()->pluck('account', 'id');
|
||||
}
|
||||
|
||||
#[NoReturn]
|
||||
public static function setDefaultFormValues(Get $get, Set $set, ?string $old, ?string $state): void
|
||||
{
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Filament\Resources\SaleResource\Pages;
|
||||
use App\Models\Account;
|
||||
use App\Models\Branch;
|
||||
use App\Models\Client;
|
||||
use App\Models\Discount;
|
||||
use App\Models\Sale;
|
||||
use Awcodes\TableRepeater\Components\TableRepeater;
|
||||
use Awcodes\TableRepeater\Header;
|
||||
@@ -30,17 +31,17 @@ class SaleResource extends Resource
|
||||
|
||||
protected static bool $shouldRegisterNavigation = false;
|
||||
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Select::make('client')
|
||||
->default(request()->query('client_id'))
|
||||
->default(fn () => request()->integer('client_id'))
|
||||
->options(Client::query()->get()->pluck('company', 'id'))
|
||||
->afterStateUpdated(function ($set, $get) {
|
||||
$set('branch_id', '');
|
||||
})
|
||||
->disabled()
|
||||
->required()
|
||||
->live(),
|
||||
Select::make('branch_id')
|
||||
@@ -72,6 +73,8 @@ class SaleResource extends Resource
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static function getSeries(Get $get): string
|
||||
{
|
||||
$branch = Branch::find($get('branch_id'));
|
||||
@@ -98,6 +101,7 @@ class SaleResource extends Resource
|
||||
Header::make('Output Tax'),
|
||||
Header::make('Withholding Tax'),
|
||||
Header::make('Discount'),
|
||||
Header::make('Discount Type'),
|
||||
Header::make('Net Amount'),
|
||||
];
|
||||
}
|
||||
@@ -136,7 +140,7 @@ class SaleResource extends Resource
|
||||
->numeric()
|
||||
->nullable()
|
||||
->live()
|
||||
->readOnly()
|
||||
->readOnly(fn (Get $get) => $get('exempt') == 0)
|
||||
->default(0),
|
||||
Hidden::make('happened_on')->default(fn (Get $get) => $get('../../happened_on')),
|
||||
Hidden::make('with_discount')->default(fn (Get $get) => $get('../../with_discount')),
|
||||
@@ -159,6 +163,9 @@ class SaleResource extends Resource
|
||||
->readOnly()
|
||||
->visible(fn (Get $get) => $get('../../with_discount'))
|
||||
->live(),
|
||||
Select::make('discount_type')
|
||||
->options(fn (Get $get) => static::getDiscountOptions($get))
|
||||
->visible(fn (Get $get) => $get('../../with_discount')),
|
||||
TextInput::make('net_amount')->numeric()->default(0),
|
||||
];
|
||||
}
|
||||
@@ -184,6 +191,13 @@ class SaleResource extends Resource
|
||||
return $query->get()->pluck('account', 'id');
|
||||
}
|
||||
|
||||
private static function getDiscountOptions(Get $get)
|
||||
{
|
||||
$query = Discount::query()->where('client_id', $get('../../client'));
|
||||
|
||||
return $query->pluck('discount', 'id');
|
||||
}
|
||||
|
||||
private static function setDefaultFormValues(Get $get, Set $set, ?string $old, ?string $state)
|
||||
{
|
||||
$exempt = (float) $get('exempt');
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Filament\Resources;
|
||||
|
||||
use App\Commands\Transmittal\GenerateTransmittalSeries;
|
||||
use App\Commands\Transmittal\StoreTransmittalCommand;
|
||||
use App\Exports\TransmittalsExport;
|
||||
use App\Filament\Resources\TransmittalResource\Pages;
|
||||
use App\Jobs\ExportCompleteJob;
|
||||
use App\Models\Branch;
|
||||
@@ -26,6 +25,7 @@ use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Malzariey\FilamentDaterangepickerFilter\Filters\DateRangeFilter;
|
||||
|
||||
class TransmittalResource extends Resource
|
||||
@@ -147,13 +147,11 @@ class TransmittalResource extends Resource
|
||||
|
||||
public static function exportTransmittal(array $id): void
|
||||
{
|
||||
$recipient = auth()->user();
|
||||
$recipient = Auth::user();
|
||||
|
||||
static::generateExportNotification();
|
||||
|
||||
(new TransmittalsExport([$id]))->store('public/transmittal-export.xlsx')->chain([
|
||||
app(ExportCompleteJob::class, ['user' => $recipient]),
|
||||
]);
|
||||
ExportCompleteJob::dispatch($recipient, Arr::flatten($id));
|
||||
}
|
||||
|
||||
public static function generateExportNotification(): Notification
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Transmittal;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Filament\Notifications\Actions\Action as NotificationAction;
|
||||
use Filament\Notifications\Notification;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@@ -13,30 +15,37 @@ use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ExportCompleteJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(private $user)
|
||||
public function __construct(private $user, private array $ids)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Notification::make()->success()
|
||||
$transmittals = Transmittal::query()
|
||||
->with(['client', 'branch', 'files.notes', 'files.remarks'])
|
||||
->whereIn('id', $this->ids)
|
||||
->get();
|
||||
|
||||
$pdf = Pdf::loadView('transmittal.export.transmittal-export-pdf', [
|
||||
'transmittals' => $transmittals,
|
||||
]);
|
||||
|
||||
Storage::disk('public')->put('transmittal-export.pdf', $pdf->output());
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('Export Completed')
|
||||
->actions([
|
||||
NotificationAction::make('download_transmittal-export.xlsx')
|
||||
->label('Download File')
|
||||
->url(url: Storage::url('public/transmittal-export.xlsx') ,shouldOpenInNewTab: true)
|
||||
->markAsRead(),
|
||||
]
|
||||
)
|
||||
NotificationAction::make('download_transmittal-export.pdf')
|
||||
->label('Download File')
|
||||
->url(Storage::url('transmittal-export.pdf'), true)
|
||||
->markAsRead(),
|
||||
])
|
||||
->sendToDatabase($this->user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,4 +78,9 @@ class Client extends Model
|
||||
{
|
||||
return $this->hasManyThrough(Journal::class, Branch::class);
|
||||
}
|
||||
|
||||
public function discounts(): HasMany
|
||||
{
|
||||
return $this->hasMany(Discount::class);
|
||||
}
|
||||
}
|
||||
|
||||
10
app/Models/Discount.php
Normal file
10
app/Models/Discount.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Discount extends Model
|
||||
{
|
||||
protected $fillable = ['discount'];
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"awcodes/filament-table-repeater": "^3.0",
|
||||
"barryvdh/laravel-dompdf": "^2.0",
|
||||
"bezhansalleh/filament-shield": "^3.2",
|
||||
"filament/filament": "^3.2",
|
||||
"laravel/framework": "^11.9",
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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('discounts', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->text('discount');
|
||||
$table->foreignId('client_id')->constrained()->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('discounts');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Transmittal Report</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: DejaVu Sans, sans-serif;
|
||||
font-size: 12px;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.header-logo {
|
||||
height: 60px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.header-title {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #000;
|
||||
padding: 4px;
|
||||
}
|
||||
th {
|
||||
text-transform: capitalize;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<img src="{{ public_path('images/logo.png') }}" alt="Logo" class="header-logo">
|
||||
<div class="header-title">Transmittal Report</div>
|
||||
</div>
|
||||
|
||||
@include('transmittal.export.transmittal-export-table', ['transmittals' => $transmittals])
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user