feat: initial commit

This commit is contained in:
JP
2024-08-05 08:04:35 +08:00
parent 0f3c3db73b
commit 140e821e0c
194 changed files with 14509 additions and 254 deletions

View File

@@ -0,0 +1,10 @@
<?php
namespace App\Actions;
use Spatie\LaravelData\Data;
abstract class BaseAction
{
abstract public function __invoke(Data $payload, \Closure $next) ;
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Actions\Branch;
use App\Actions\BaseAction;
use App\Commands\Branches\CreateBranchCommand;
use App\DataObjects\CreateBranchDTO;
use Exception;
use Spatie\LaravelData\Data;
class StoreBranch extends BaseAction
{
public function __construct(private CreateBranchCommand $createBranchCommand) {}
/**
* @throws Exception
*/
public function __invoke(CreateBranchDTO | Data $payload, \Closure $next)
{
try {
$payload->branch = $this->createBranchCommand->execute($payload->data);
return $next($payload);
} catch (Exception $exception)
{
throw new \LogicException('Error Storing Branch: ' . $exception->getMessage());
}
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Actions\Branch;
use App\Actions\BaseAction;
use App\Commands\Series\CreateSeriesCommand;
use App\DataObjects\CreateBranchDTO;
use App\DataObjects\CreateSeriesDTO;
use Spatie\LaravelData\Data;
class StoreBranchSeries extends BaseAction
{
public function __construct(
private CreateSeriesCommand $createSeriesCommand
) {}
public function __invoke(CreateBranchDTO|Data $payload, \Closure $next)
{
try {
$seriesPayload = CreateSeriesDTO::from(['branch_id' => $payload->branch->id, 'series' => $payload->data['series'], 'is_start' => true]);
$payload->series = $this->createSeriesCommand->execute($seriesPayload->toArray());
return $next($payload);
} catch (\Exception $exception) {
throw new \LogicException('Failed to create branch series', $exception->getMessage());
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Actions\Transmittal;
use App\Actions\BaseAction;
use App\Commands\Transmittal\StoreTransmittalCommand;
use App\DataObjects\CreateTransmittalDTO;
use Illuminate\Support\Arr;
use Spatie\LaravelData\Data;
class CreateTransmittal extends BaseAction
{
public function __construct(
private readonly StoreTransmittalCommand $storeTransmittalCommand
) {}
public function __invoke(CreateTransmittalDTO | Data $payload, \Closure $next)
{
try {
$payload->transmittal = $this->storeTransmittalCommand->execute(Arr::except($payload->data, ['files']));
return $next($payload);
} catch (\Exception $exception)
{
throw new \LogicException('Error creating transmittal: ' . $exception->getMessage());
}
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace App\Actions\Transmittal;
use App\Actions\BaseAction;
use App\Commands\Transmittal\StoreCommentCommand;
use App\Commands\Transmittal\StoreFileCommand;
use App\Commands\Transmittal\StoreRemarkCommand;
use App\DataObjects\CreateCommentDTO;
use App\DataObjects\CreateFileDTO;
use App\DataObjects\CreateRemarkDTO;
use App\DataObjects\CreateTransmittalDTO;
use Illuminate\Support\Arr;
use Spatie\LaravelData\Data;
class CreateTransmittalFiles extends BaseAction
{
public function __construct(
private readonly StoreFileCommand $storeFileCommand,
private readonly StoreCommentCommand $storeCommentCommand,
private readonly StoreRemarkCommand $storeRemarkCommand
) {}
public function __invoke(CreateTransmittalDTO | Data $payload, \Closure $next)
{
try {
$files = Arr::only($payload->data, 'files');
$filesCreated = [];
foreach ($files as $file) {
//save each file
$filePayload = new CreateFileDTO(transmittal_id: $payload->transmittal->id, description: Arr::first($file)['description']);
$fileCreated = $this->storeFileCommand->execute($filePayload->except('file')->toArray());
$comments = Arr::first($file)['comments'];
$remarks = Arr::first($file)['remarks'];
//save each comments of each file
foreach ($comments as $key => $comment) {
$commentPayload = new CreateCommentDTO(file_id: $fileCreated->id, comment: $comment['comment']);
$this->storeCommentCommand->execute($commentPayload->except('commentModel')->toArray());
}
//save each remarks of each file
foreach ($remarks as $key => $remark) {
$remarkPayload = new CreateRemarkDTO(file_id: $fileCreated->id, remark: $remark['remark']);
$this->storeRemarkCommand->execute($remarkPayload->except('remarkModel')->toArray());
}
$filesCreated[] = $fileCreated;
}
$payload->files = $filesCreated;
return $next($payload);
} catch (\Exception $exception)
{
throw new \LogicException('Error creating transmittal files: ' . $exception->getMessage());
}
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Commands\Branches;
use App\Commands\Command;
use App\Models\Branch;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
class CreateBranchCommand implements Command
{
public function execute(array $data): \Illuminate\Database\Eloquent\Model
{
return DB::transaction(
callback: fn() => Branch::query()->updateOrCreate(['id' => $data['id'] ?? null], Arr::except($data, ['series'])),
attempts: 2
);
}
}

11
app/Commands/Command.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
namespace App\Commands;
use Illuminate\Database\Eloquent\Model;
interface Command
{
public function execute(array $data) : mixed;
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Commands\Series;
use App\Actions\BaseAction;
use App\Commands\Command;
use App\Models\Series;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class CreateSeriesCommand implements Command
{
public function execute($data) : Model
{
return DB::transaction(
callback: fn () => Series::query()->create($data),
attempts: 2
);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Commands\Transmittal;
use App\Commands\Command;
use App\Models\Transmittal;
use Illuminate\Database\Eloquent\Model;
class GenerateTransmittalSeries implements Command
{
public function execute(array $data): string
{
$lastSeries = Transmittal::query()->orderBy('id', 'desc')->first();
if ($lastSeries) {
$series = explode('-', $lastSeries->series)[1];
return 'TR-'.str_pad((int)$series + 1, 10, '0', STR_PAD_LEFT);
}
return $series = 'TR-'.str_pad(1, 10, '0', STR_PAD_LEFT);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Commands\Transmittal;
use App\Commands\Command;
use App\Models\Comment;
use Illuminate\Support\Facades\DB;
class StoreCommentCommand implements Command
{
public function execute(array $data): mixed
{
return DB::transaction(
callback: fn () => Comment::query()->create($data),
attempts: 2
);
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Commands\Transmittal;
use App\Commands\Command;
use App\Models\File;
use Illuminate\Support\Facades\DB;
use function PHPUnit\Framework\callback;
class StoreFileCommand implements Command
{
public function execute(array $data): mixed
{
return DB::transaction(
callback: fn () => File::query()->create($data),
attempts: 2
);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Commands\Transmittal;
use App\Commands\Command;
use App\Models\Remark;
use Illuminate\Support\Facades\DB;
class StoreRemarkCommand implements Command
{
public function execute(array $data): mixed
{
return DB::transaction(
callback: fn () => Remark::query()->create($data),
attempts: 2
);
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Commands\Transmittal;
use App\Commands\Command;
use App\Models\Transmittal;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class StoreTransmittalCommand implements Command
{
public function execute(array $data): Model
{
return DB::transaction(
callback: fn () => Transmittal::query()->updateOrCreate(['id' => $data['id'] ?? null], $data),
attempts: 2
);
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\DataObjects;
use Illuminate\Database\Eloquent\Model;
use Spatie\LaravelData\Data;
class CreateBranchDTO extends Data
{
public function __construct(
public array $data,
public ?Model $branch = null,
public ?Model $series = null
) {}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\DataObjects;
use Illuminate\Database\Eloquent\Model;
use Spatie\LaravelData\Data;
class CreateCommentDTO extends Data
{
public function __construct(
public int $file_id,
public string $comment,
public ?Model $commentModel = null
) {}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\DataObjects;
use Illuminate\Database\Eloquent\Model;
use Spatie\LaravelData\Data;
class CreateFileDTO extends Data
{
public function __construct(
public int $transmittal_id,
public string $description,
public ?Model $file = null,
) {}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\DataObjects;
use Illuminate\Database\Eloquent\Model;
use Spatie\LaravelData\Data;
class CreateRemarkDTO extends Data
{
public function __construct(
public int $file_id,
public string $remark,
public ?Model $remarkModel = null
) {}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\DataObjects;
use Spatie\LaravelData\Data;
class CreateSeriesDTO extends Data
{
public function __construct(
public ?int $id,
public string $series,
public ?int $branch_id,
public ?int $sale_id,
public ?bool $is_start,
) {}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\DataObjects;
use Illuminate\Database\Eloquent\Model;
use Spatie\LaravelData\Data;
class CreateTransmittalDTO extends Data
{
/**
* @param array $data
* @param Model|null $transmittal
* @param Model[]|null $files
*/
public function __construct(
public array $data,
public ?Model $transmittal = null,
public ?array $files = null,
) {}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace App\Exports;
use App\Models\Transmittal;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\View;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\FromView;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithDefaultStyles;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use PhpOffice\PhpSpreadsheet\Exception;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Style;
class TransmittalsExport implements FromCollection, WithMapping, ShouldAutoSize, ShouldQueue, WithDefaultStyles, WithHeadings
{
use Exportable, Queueable, SerializesModels;
public function __construct(
private readonly array $id
)
{}
public function view(): \Illuminate\Contracts\View\View
{
$transmittals = Transmittal::query()->with(['client','branch', 'files.notes', 'files.remarks'])->whereIn('id', Arr::flatten($this->id))->get();
return View::make('transmittal.export.transmittal-export-table')->with(['transmittals' => $transmittals]);
}
/**
* @throws Exception
*/
public function defaultStyles(Style $defaultStyle)
{
return $defaultStyle->applyFromArray([
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
'vertical' => Alignment::VERTICAL_CENTER,
],
]);
}
public function headings(): array
{
return [
'series',
'files',
'notes',
'remarks'
];
}
public function collection()
{
$transmittals = Transmittal::query()->with(['client','branch', 'files.notes', 'files.remarks'])
->withCount(['files','notes','remarks'])->with(['files' => function($files) {
$files->withCount(['notes','remarks']);
}])
->whereIn('id', Arr::flatten($this->id))->get();
return $transmittals;
}
public function map($transmittal): array
{
$data = [];
$firstFile = $transmittal->files->first();
$data[] = [$transmittal->series, $firstFile?->description, $firstFile->notes->first()?->comment, $firstFile->remarks->first()?->remark];
$fileRowCounter = 0;
foreach ($transmittal->files as $file) {
$notes = $file->notes->pluck('comment');
$remarks = $file->remarks->pluck('remark');
$fileNoteCount = count($notes);
$fileRemarkCount = count($remarks);
$fileRowCount = $fileNoteCount;
if($fileRemarkCount > $fileNoteCount) {
$fileRowCount = $fileRemarkCount;
}
if($fileRowCounter != 0) {
$data[] = [
'',
$file->description ,
$notes[$fileRowCounter - 1] ?? '',
$remarks[$fileRowCounter - 1] ?? '',
];
for ($i = 0; $i < $fileRowCount; $i++) {
$data[] = [
'',
'',
$notes[$i] ?? '',
$remarks[$i] ?? '',
];
}
}
$fileRowCounter++;
}
return $data;
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Filament\Exports;
use App\Models\ClientAccounts;
use Filament\Actions\Exports\ExportColumn;
use Filament\Actions\Exports\Exporter;
use Filament\Actions\Exports\Models\Export;
class ClientAccountsExporter extends Exporter
{
protected static ?string $model = ClientAccounts::class;
public static function getColumns(): array
{
return [
ExportColumn::make('id')
->label('ID'),
ExportColumn::make('account'),
ExportColumn::make('description'),
ExportColumn::make('acountType.type'),
ExportColumn::make('client.company'),
ExportColumn::make('created_at'),
ExportColumn::make('updated_at'),
];
}
public static function getCompletedNotificationBody(Export $export): string
{
$body = 'Your client accounts export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.';
if ($failedRowsCount = $export->getFailedRowsCount()) {
$body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.';
}
return $body;
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Filament\Exports;
use App\Models\Client;
use Filament\Actions\Exports\ExportColumn;
use Filament\Actions\Exports\Exporter;
use Filament\Actions\Exports\Models\Export;
class ClientExporter extends Exporter
{
protected static ?string $model = Client::class;
public static function getColumns(): array
{
return [
ExportColumn::make('id')
->label('ID'),
ExportColumn::make('firstname'),
ExportColumn::make('lastname'),
ExportColumn::make('middlename'),
ExportColumn::make('company'),
ExportColumn::make('type.type'),
ExportColumn::make('created_at'),
ExportColumn::make('updated_at'),
];
}
public static function getCompletedNotificationBody(Export $export): string
{
$body = 'Your client export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.';
if ($failedRowsCount = $export->getFailedRowsCount()) {
$body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.';
}
return $body;
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\BranchResource\Pages;
use App\Filament\Resources\BranchResource\RelationManagers;
use App\Models\Branch;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class BranchResource extends Resource
{
protected static ?string $model = Branch::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
protected static bool $shouldRegisterNavigation = false;
public static function form(Form $form): Form
{
return $form
->schema([
//
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
//
])
->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\ListBranches::route('/'),
'create' => Pages\CreateBranch::route('/create'),
'edit' => Pages\EditBranch::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Filament\Resources\BranchResource\Pages;
use App\Filament\Resources\BranchResource;
use Filament\Actions;
use Filament\Resources\Pages\CreateRecord;
class CreateBranch extends CreateRecord
{
protected static string $resource = BranchResource::class;
}

View File

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

View File

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

View File

@@ -0,0 +1,101 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\ClientResource\RelationManagers\AccountsRelationManager;
use App\Filament\Resources\ClientResource\RelationManagers\BranchesRelationManager;
use App\Filament\Resources\ClientResource\RelationManagers\TransmittalsRelationManager;
use App\Models\Client;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Infolists\Components\Grid;
use Filament\Infolists\Components\Section;
use Filament\Infolists\Components\Split;
use Filament\Infolists\Components\TextEntry;
use Filament\Infolists\Infolist;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class ClientResource extends Resource
{
protected static ?string $model = Client::class;
protected static ?string $navigationIcon = 'heroicon-o-user';
public static function authorizeView(Model $record): void
{
parent::authorizeView($record); // TODO: Change the autogenerated stub
}
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('firstname')->label('First Name')->required(),
Forms\Components\TextInput::make('middlename')->label('Middle Name')->nullable(),
Forms\Components\TextInput::make('lastname')->label('Last Name')->required(),
Forms\Components\Grid::make()->schema([
Forms\Components\TextInput::make('company')->label('Company')->required(),
Forms\Components\Select::make('type_id')
->relationship('type', 'type')
->label('Type')->required(),
])->columns(2)
])->columns(3);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('firstname')->label('First Name')->searchable(),
Tables\Columns\TextColumn::make('middlename')->label('Middle Name'),
Tables\Columns\TextColumn::make('lastname')->label('Last Name'),
Tables\Columns\TextColumn::make('company')->label('Company')->searchable(),
Tables\Columns\TextColumn::make('type.type')->label('Type'),
])
->filters([
Tables\Filters\Filter::make('Vatable')
->query(fn (Builder $query) => $query->orWhereHas('type', function (Builder $query) {
$query->where('type', 'Vatable');
}) ),
Tables\Filters\Filter::make('Non-Vatable')
->query(fn (Builder $query) => $query->orWhereHas('type', function (Builder $query) {
$query->where('type', 'Non Vatable');
}) )
])
->actions([
Tables\Actions\ViewAction::make(),
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make()->requiresConfirmation()
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
AccountsRelationManager::class,
BranchesRelationManager::class,
TransmittalsRelationManager::class
];
}
public static function getPages(): array
{
return [
'view' => \App\Filament\Resources\ClientResource\Pages\ViewClient:: route('/{record}'),
'edit' => \App\Filament\Resources\ClientResource\Pages\EditClient::route('/{record}/edit'),
'index' => \App\Filament\Resources\ClientResource\Pages\ListClients::route('/'),
];
}
}

View File

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

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Filament\Resources\ClientResource\Pages;
use App\Filament\Resources\ClientResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditClient extends EditRecord
{
protected static string $resource = ClientResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make()->icon('heroicon-s-trash')->requiresConfirmation(),
];
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Filament\Resources\ClientResource\Pages;
use App\Filament\Exports\ClientExporter;
use App\Filament\Resources\ClientResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListClients extends ListRecords
{
protected static string $resource = ClientResource::class;
protected function getHeaderActions(): array
{
return [
Actions\ExportAction::make('Export clients')
->exporter(ClientExporter::class)
->formats([
Actions\Exports\Enums\ExportFormat::Csv,
]),
Actions\CreateAction::make()->slideOver(),
];
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Filament\Resources\ClientResource\Pages;
use App\Filament\Resources\ClientResource;
use App\Filament\Resources\ClientResource\RelationManagers\AccountsRelationManager;
use App\Filament\Resources\ClientResource\RelationManagers\BranchesRelationManager;
use App\Filament\Resources\ClientResource\RelationManagers\TransmittalsRelationManager;
use App\Models\Account;
use App\Models\Transmittal;
use Filament\Actions;
use Filament\Infolists\Components\Grid;
use Filament\Infolists\Components\RepeatableEntry;
use Filament\Infolists\Components\Section;
use Filament\Infolists\Components\TextEntry;
use Filament\Infolists\Infolist;
use Filament\Resources\Pages\ViewRecord;
class ViewClient extends ViewRecord
{
protected static string $resource = ClientResource::class;
public function infolist(Infolist $infolist): Infolist
{
return $infolist
->schema([
Section::make()->schema([
Grid::make()->schema([
TextEntry::make('firstname')->label('First Name'),
TextEntry::make('middlename')->label('Middle Name'),
TextEntry::make('lastname')->label('Last Name'),
TextEntry::make('company')->label('Company'),
TextEntry::make('type.type')->label('Type'),
])->columns(3),
]),
Section::make('Branches')->schema([
RepeatableEntry::make('branches')
->schema([
TextEntry::make('code')->label('Branch Code'),
TextEntry::make('current_series')->label('Branch Current Series'),
])
->hiddenLabel()
->grid(2)
])->collapsible()
]);
}
public function getRelationManagers(): array
{
return [
AccountsRelationManager::class,
TransmittalsRelationManager::class,
];
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace App\Filament\Resources\ClientResource\RelationManagers;
use App\Filament\Exports\ClientAccountsExporter;
use App\Models\Account;
use Filament\Actions\Exports\Enums\ExportFormat;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;
class AccountsRelationManager extends RelationManager
{
protected static string $relationship = 'accounts';
public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('account')->required(),
Forms\Components\Textarea::make('description')->nullable(),
Forms\Components\Select::make('account_type_id')
->relationship('accountType','type')
->required(),
Forms\Components\Select::make('normal_balance')->options(
['debit' => 'Debit','credit' => 'Credit']
)->required()
]);
}
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('client_id')
->columns([
Tables\Columns\TextColumn::make('account')->description(fn (Account $record): string => $record->description),
Tables\Columns\TextColumn::make('accountType.type'),
Tables\Columns\TextColumn::make('normal_balance'),
])
->filters([
//
])
->headerActions([
Tables\Actions\ExportAction::make('Export Accounts')->exporter(ClientAccountsExporter::class)->formats([ExportFormat::Csv]),
Tables\Actions\CreateAction::make()->label('New Account')->icon('heroicon-o-plus')->slideOver(),
])
->actions([
Tables\Actions\EditAction::make()->slideOver(),
Tables\Actions\DeleteAction::make()->requiresConfirmation(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make()->icon('heroicon-s-trash')->requiresConfirmation(),
]),
]);
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace App\Filament\Resources\ClientResource\RelationManagers;
use App\DataObjects\CreateBranchDTO;
use App\Processes\Branch\CreateBranchProcess;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Support\RawJs;
use Filament\Tables;
use Filament\Tables\Table;
class BranchesRelationManager extends RelationManager
{
protected static string $relationship = 'branches';
protected static bool $shouldCheckPolicyExistence = true;
protected CreateBranchProcess $createBranchProcess;
public function __construct() {
$this->createBranchProcess = new CreateBranchProcess();
}
public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('code')->required()->unique('branches','code'),
Forms\Components\TextInput::make('series')->label('Current Series')
->required()
->numeric()
->integer()
->maxLength(6)
->minLength(1)
->mask(RawJs::make(<<<'JS'
'999999'
JS))
])->columns(1);
}
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('client_id')
->columns([
Tables\Columns\TextColumn::make('code')->label('Branch Code'),
Tables\Columns\TextColumn::make('current_series')->label('Current Series'),
])
->filters([
//
])
->headerActions([
Tables\Actions\CreateAction::make()
->mutateFormDataUsing(function (array $data): array {
$data['client_id'] = $this->ownerRecord->id;
return $data;
})
->using(function($data) {
$payload = new CreateBranchDTO(data: $data);
return $this->createBranchProcess->run($payload)->branch;
}),
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,48 @@
<?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;
class ExpensesRelationManager extends RelationManager
{
protected static string $relationship = 'expenses';
public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('client_id')
->required()
->maxLength(255),
]);
}
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('client_id')
->columns([
Tables\Columns\TextColumn::make('client_id'),
])
->filters([
//
])
->headerActions([
Tables\Actions\CreateAction::make(),
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace App\Filament\Resources\ClientResource\RelationManagers;
use App\Commands\Transmittal\GenerateTransmittalSeries;
use App\Commands\Transmittal\StoreTransmittalCommand;
use App\DataObjects\CreateTransmittalDTO;
use App\Filament\Resources\TransmittalResource;
use App\Models\Branch;
use App\Models\Client;
use App\Models\Transmittal;
use App\Processes\Transmittal\CreateTransmittalProcess;
use Filament\Actions\Action;
use Filament\Actions\CreateAction;
use Filament\Forms;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Model;
class TransmittalsRelationManager extends RelationManager
{
protected static string $relationship = 'transmittals';
public static function canViewForRecord(Model $ownerRecord, string $pageClass): bool
{
return auth()->user()->can('update_transmittal');
}
protected CreateTransmittalProcess $transmittalProcess;
public function __construct()
{
$this->transmittalProcess = new CreateTransmittalProcess();
}
public function form(Form $form): Form
{
return TransmittalResource::form($form)
->fill(
['client_id' => $this->getOwnerRecord()->id]
);
}
public function table(Table $table): Table
{
return TransmittalResource::table($table)->headerActions([
Tables\Actions\Action::make('New Transmittal')->action('openCreateForm')
]);
}
public function openCreateForm() {
return redirect()->route('filament.admin.resources.transmittals.create');
}
}

View File

@@ -0,0 +1,400 @@
<?php
namespace App\Filament\Resources\Shield;
use App\Filament\Resources\Shield\RoleResource\Pages;
use BezhanSalleh\FilamentShield\Contracts\HasShieldPermissions;
use BezhanSalleh\FilamentShield\Facades\FilamentShield;
use BezhanSalleh\FilamentShield\FilamentShieldPlugin;
use BezhanSalleh\FilamentShield\Forms\ShieldSelectAllToggle;
use BezhanSalleh\FilamentShield\Support\Utils;
use Filament\Forms;
use Filament\Forms\Components\Component;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\HtmlString;
use Illuminate\Support\Str;
class RoleResource extends Resource implements HasShieldPermissions
{
protected static ?string $recordTitleAttribute = 'name';
public static function getPermissionPrefixes(): array
{
return [
'view',
'view_any',
'create',
'update',
'delete',
'delete_any',
];
}
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Grid::make()
->schema([
Forms\Components\Section::make()
->schema([
Forms\Components\TextInput::make('name')
->label(__('filament-shield::filament-shield.field.name'))
->unique(ignoreRecord: true)
->required()
->maxLength(255),
Forms\Components\TextInput::make('guard_name')
->label(__('filament-shield::filament-shield.field.guard_name'))
->default(Utils::getFilamentAuthGuard())
->nullable()
->maxLength(255),
ShieldSelectAllToggle::make('select_all')
->onIcon('heroicon-s-shield-check')
->offIcon('heroicon-s-shield-exclamation')
->label(__('filament-shield::filament-shield.field.select_all.name'))
->helperText(fn (): HtmlString => new HtmlString(__('filament-shield::filament-shield.field.select_all.message')))
->dehydrated(fn ($state): bool => $state),
])
->columns([
'sm' => 2,
'lg' => 3,
]),
]),
Forms\Components\Tabs::make('Permissions')
->contained()
->tabs([
static::getTabFormComponentForResources(),
static::getTabFormComponentForPage(),
static::getTabFormComponentForWidget(),
static::getTabFormComponentForCustomPermissions(),
])
->columnSpan('full'),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name')
->badge()
->label(__('filament-shield::filament-shield.column.name'))
->formatStateUsing(fn ($state): string => Str::headline($state))
->colors(['primary'])
->searchable(),
Tables\Columns\TextColumn::make('guard_name')
->badge()
->label(__('filament-shield::filament-shield.column.guard_name')),
Tables\Columns\TextColumn::make('permissions_count')
->badge()
->label(__('filament-shield::filament-shield.column.permissions'))
->counts('permissions')
->colors(['success']),
Tables\Columns\TextColumn::make('updated_at')
->label(__('filament-shield::filament-shield.column.updated_at'))
->dateTime(),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
])
->bulkActions([
Tables\Actions\DeleteBulkAction::make(),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListRoles::route('/'),
'create' => Pages\CreateRole::route('/create'),
'view' => Pages\ViewRole::route('/{record}'),
'edit' => Pages\EditRole::route('/{record}/edit'),
];
}
public static function getCluster(): ?string
{
return Utils::getResourceCluster() ?? static::$cluster;
}
public static function getModel(): string
{
return Utils::getRoleModel();
}
public static function getModelLabel(): string
{
return __('filament-shield::filament-shield.resource.label.role');
}
public static function getPluralModelLabel(): string
{
return __('filament-shield::filament-shield.resource.label.roles');
}
public static function shouldRegisterNavigation(): bool
{
return Utils::isResourceNavigationRegistered();
}
public static function getNavigationGroup(): ?string
{
return 'Security Settings';
}
public static function getNavigationLabel(): string
{
return __('filament-shield::filament-shield.nav.role.label');
}
public static function getNavigationIcon(): string
{
return __('filament-shield::filament-shield.nav.role.icon');
}
public static function getNavigationSort(): ?int
{
return Utils::getResourceNavigationSort();
}
public static function getSlug(): string
{
return Utils::getResourceSlug();
}
public static function getNavigationBadge(): ?string
{
return Utils::isResourceNavigationBadgeEnabled()
? strval(static::getEloquentQuery()->count())
: null;
}
public static function isScopedToTenant(): bool
{
return Utils::isScopedToTenant();
}
public static function canGloballySearch(): bool
{
return Utils::isResourceGloballySearchable() && count(static::getGloballySearchableAttributes()) && static::canViewAny();
}
public static function getResourceEntitiesSchema(): ?array
{
return collect(FilamentShield::getResources())
->sortKeys()
->map(function ($entity) {
$sectionLabel = strval(
static::shield()->hasLocalizedPermissionLabels()
? FilamentShield::getLocalizedResourceLabel($entity['fqcn'])
: $entity['model']
);
return Forms\Components\Section::make($sectionLabel)
->description(fn () => new HtmlString('<span style="word-break: break-word;">' . Utils::showModelPath($entity['fqcn']) . '</span>'))
->compact()
->schema([
static::getCheckBoxListComponentForResource($entity),
])
->columnSpan(static::shield()->getSectionColumnSpan())
->collapsible();
})
->toArray();
}
public static function getResourceTabBadgeCount(): ?int
{
return collect(FilamentShield::getResources())
->map(fn ($resource) => count(static::getResourcePermissionOptions($resource)))
->sum();
}
public static function getResourcePermissionOptions(array $entity): array
{
return collect(Utils::getResourcePermissionPrefixes($entity['fqcn']))
->flatMap(function ($permission) use ($entity) {
$name = $permission . '_' . $entity['resource'];
$label = static::shield()->hasLocalizedPermissionLabels()
? FilamentShield::getLocalizedResourcePermissionLabel($permission)
: $name;
return [
$name => $label,
];
})
->toArray();
}
public static function setPermissionStateForRecordPermissions(Component $component, string $operation, array $permissions, ?Model $record): void
{
if (in_array($operation, ['edit', 'view'])) {
if (blank($record)) {
return;
}
if ($component->isVisible() && count($permissions) > 0) {
$component->state(
collect($permissions)
/** @phpstan-ignore-next-line */
->filter(fn ($value, $key) => $record->checkPermissionTo($key))
->keys()
->toArray()
);
}
}
}
public static function getPageOptions(): array
{
return collect(FilamentShield::getPages())
->flatMap(fn ($page) => [
$page['permission'] => static::shield()->hasLocalizedPermissionLabels()
? FilamentShield::getLocalizedPageLabel($page['class'])
: $page['permission'],
])
->toArray();
}
public static function getWidgetOptions(): array
{
return collect(FilamentShield::getWidgets())
->flatMap(fn ($widget) => [
$widget['permission'] => static::shield()->hasLocalizedPermissionLabels()
? FilamentShield::getLocalizedWidgetLabel($widget['class'])
: $widget['permission'],
])
->toArray();
}
public static function getCustomPermissionOptions(): ?array
{
return FilamentShield::getCustomPermissions()
->mapWithKeys(fn ($customPermission) => [
$customPermission => static::shield()->hasLocalizedPermissionLabels() ? str($customPermission)->headline()->toString() : $customPermission,
])
->toArray();
}
public static function getTabFormComponentForResources(): Component
{
return static::shield()->hasSimpleResourcePermissionView()
? static::getTabFormComponentForSimpleResourcePermissionsView()
: Forms\Components\Tabs\Tab::make('resources')
->label(__('filament-shield::filament-shield.resources'))
->visible(fn (): bool => (bool) Utils::isResourceEntityEnabled())
->badge(static::getResourceTabBadgeCount())
->schema([
Forms\Components\Grid::make()
->schema(static::getResourceEntitiesSchema())
->columns(static::shield()->getGridColumns()),
]);
}
public static function getCheckBoxListComponentForResource(array $entity): Component
{
$permissionsArray = static::getResourcePermissionOptions($entity);
return static::getCheckboxListFormComponent($entity['resource'], $permissionsArray, false);
}
public static function getTabFormComponentForPage(): Component
{
$options = static::getPageOptions();
$count = count($options);
return Forms\Components\Tabs\Tab::make('pages')
->label(__('filament-shield::filament-shield.pages'))
->visible(fn (): bool => (bool) Utils::isPageEntityEnabled() && $count > 0)
->badge($count)
->schema([
static::getCheckboxListFormComponent('pages_tab', $options),
]);
}
public static function getTabFormComponentForWidget(): Component
{
$options = static::getWidgetOptions();
$count = count($options);
return Forms\Components\Tabs\Tab::make('widgets')
->label(__('filament-shield::filament-shield.widgets'))
->visible(fn (): bool => (bool) Utils::isWidgetEntityEnabled() && $count > 0)
->badge($count)
->schema([
static::getCheckboxListFormComponent('widgets_tab', $options),
]);
}
public static function getTabFormComponentForCustomPermissions(): Component
{
$options = static::getCustomPermissionOptions();
$count = count($options);
return Forms\Components\Tabs\Tab::make('custom')
->label(__('filament-shield::filament-shield.custom'))
->visible(fn (): bool => (bool) Utils::isCustomPermissionEntityEnabled() && $count > 0)
->badge($count)
->schema([
static::getCheckboxListFormComponent('custom_permissions', $options),
]);
}
public static function getTabFormComponentForSimpleResourcePermissionsView(): Component
{
$options = FilamentShield::getAllResourcePermissions();
$count = count($options);
return Forms\Components\Tabs\Tab::make('resources')
->label(__('filament-shield::filament-shield.resources'))
->visible(fn (): bool => (bool) Utils::isResourceEntityEnabled() && $count > 0)
->badge($count)
->schema([
static::getCheckboxListFormComponent('resources_tab', $options),
]);
}
public static function getCheckboxListFormComponent(string $name, array $options, bool $searchable = true): Component
{
return Forms\Components\CheckboxList::make($name)
->label('')
->options(fn (): array => $options)
->searchable($searchable)
->afterStateHydrated(
fn (Component $component, string $operation, ?Model $record) => static::setPermissionStateForRecordPermissions(
component: $component,
operation: $operation,
permissions: $options,
record: $record
)
)
->dehydrated(fn ($state) => ! blank($state))
->bulkToggleable()
->gridDirection('row')
->columns(static::shield()->getCheckboxListColumns())
->columnSpan(static::shield()->getCheckboxListColumnSpan());
}
public static function shield(): FilamentShieldPlugin
{
return FilamentShieldPlugin::get();
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Filament\Resources\Shield\RoleResource\Pages;
use App\Filament\Resources\Shield\RoleResource;
use BezhanSalleh\FilamentShield\Support\Utils;
use Filament\Resources\Pages\CreateRecord;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
class CreateRole extends CreateRecord
{
protected static string $resource = RoleResource::class;
public Collection $permissions;
protected function mutateFormDataBeforeCreate(array $data): array
{
$this->permissions = collect($data)
->filter(function ($permission, $key) {
return ! in_array($key, ['name', 'guard_name', 'select_all']);
})
->values()
->flatten()
->unique();
return Arr::only($data, ['name', 'guard_name']);
}
protected function afterCreate(): void
{
$permissionModels = collect();
$this->permissions->each(function ($permission) use ($permissionModels) {
$permissionModels->push(Utils::getPermissionModel()::firstOrCreate([
/** @phpstan-ignore-next-line */
'name' => $permission,
'guard_name' => $this->data['guard_name'],
]));
});
$this->record->syncPermissions($permissionModels);
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace App\Filament\Resources\Shield\RoleResource\Pages;
use App\Filament\Resources\Shield\RoleResource;
use BezhanSalleh\FilamentShield\Support\Utils;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
class EditRole extends EditRecord
{
protected static string $resource = RoleResource::class;
public Collection $permissions;
protected function getActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
protected function mutateFormDataBeforeSave(array $data): array
{
$this->permissions = collect($data)
->filter(function ($permission, $key) {
return ! in_array($key, ['name', 'guard_name', 'select_all']);
})
->values()
->flatten()
->unique();
return Arr::only($data, ['name', 'guard_name']);
}
protected function afterSave(): void
{
$permissionModels = collect();
$this->permissions->each(function ($permission) use ($permissionModels) {
$permissionModels->push(Utils::getPermissionModel()::firstOrCreate([
'name' => $permission,
'guard_name' => $this->data['guard_name'],
]));
});
$this->record->syncPermissions($permissionModels);
}
}

View File

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

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\Shield\RoleResource\Pages;
use App\Filament\Resources\Shield\RoleResource;
use Filament\Actions;
use Filament\Resources\Pages\ViewRecord;
class ViewRole extends ViewRecord
{
protected static string $resource = RoleResource::class;
protected function getActions(): array
{
return [
Actions\EditAction::make(),
];
}
}

View File

@@ -0,0 +1,214 @@
<?php
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\Filament\Resources\TransmittalResource\RelationManagers;
use App\Jobs\ExportCompleteJob;
use App\Models\Branch;
use App\Models\Client;
use App\Models\Transmittal;
use App\Models\User;
use Filament\Forms;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Support\Enums\FontWeight;
use Filament\Tables;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Support\Arr;
use Maatwebsite\Excel\Facades\Excel;
use Malzariey\FilamentDaterangepickerFilter\Filters\DateRangeFilter;
use YOS\FilamentExcel\Actions\Import;
class TransmittalResource extends Resource
{
protected static ?string $model = Transmittal::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
public static function form(Form $form): Form
{
return $form
->schema(static::getFormSchema());
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\Layout\Split::make([
Tables\Columns\TextColumn::make('series')
->searchable()
->label('Series')
->weight(FontWeight::Bold)
->columnSpan(2),
Tables\Columns\Layout\Stack::make([
Tables\Columns\TextColumn::make('client.company')
->searchable()
->weight(FontWeight::SemiBold)->label('Client'),
Tables\Columns\TextColumn::make('branch.code')
->searchable()
->label('Branch'),
]),
]),
Tables\Columns\Layout\View::make('transmittal.tables.collapsible-files-component')->collapsible(),
])
->filters([
SelectFilter::make('client_id')->label('Client filter')->options(function () {
return Client::query()->get()->pluck('company', 'id');
}),
SelectFilter::make('branch_id')->label('Branch Filter')->options(function () {
return Branch::query()->get()->pluck('code', 'id');
}),
SelectFilter::make('user_id')->label('Assigned To Filter')->options(function () {
return User::query()->get()->pluck('name', 'id');
}),
DateRangeFilter::make('date_created'),
DateRangeFilter::make('date_dispatch'),
DateRangeFilter::make('date_received'),
])
->heading('Transmittals')
->description('Click on toggle button at the end of table row to show additional details.')
->actions(static::getTableActions())
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
Tables\Actions\BulkAction::make('Bulk Export')->action(function ($records) {
static::exportTransmittal(Arr::flatten($records->pluck('id')));
})
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getFormSchema() : array
{
return [
Forms\Components\Select::make('client_id')
->options(function () {
return Client::query()->get()->pluck('company', 'id');
})
->label('Client')
->reactive()
->required()
->required()->columnSpan(3),
Forms\Components\Select::make('branch_id')->label('Branch')->relationship('branch')->options(function (callable $get) {
return Branch::query()->where('client_id', $get('client_id'))->get()->pluck('code', 'id');
})->required(),
Forms\Components\TextInput::make('series')->readOnly()->default((new GenerateTransmittalSeries)->execute([]))->unique('transmittals', ignoreRecord: true),
Forms\Components\DatePicker::make('date_created')
->native(false)
->required()->default(now()),
Repeater::make('files')
->relationship('files')
->label('Item')
->schema([
Textarea::make('description')->required(),
Repeater::make('comments')
->relationship('notes')
->label('Note')
->schema([
Forms\Components\TextInput::make('comment')->label('Note')->required(),
]),
Repeater::make('remarks')
->relationship('remarks')
->label('Remarks')
->schema([
Forms\Components\TextInput::make('remark')->label('Remark')->required(),
]),
])
->columns(3)
->columnSpan(3)
];
}
public static function getTableActions () : array
{
return [
Tables\Actions\Action::make('Export')->action(fn ($record) => static::exportTransmittal([$record->id])),
Tables\Actions\ViewAction::make(),
Tables\Actions\Action::make('Update Status')
->fillForm(function($record) {
return [
'user_id' => $record->user_id,
'date_dispatch' => $record->date_dispatch,
'date_received' => $record->date_received,
'received_by' => $record->received_by,
];
})
->form([
Select::make('user_id')->label('Dispatch By')
->relationship('user', 'name')
->searchable()
->preload(),
Datepicker::make('date_dispatch')->label('Dispatch Date')
->native(false)->default(now()),
TextInput::make('received_by')->label('Received By'),
Datepicker::make('date_received')->label('Date Received')->native(false),
])
->action(function ($data, $record) {
$data['id'] = $record->id;
(new StoreTransmittalCommand())->execute($data);
})
->icon('heroicon-o-pencil-square')
->slideOver()
->hidden(!auth()->user()->can('update_transmittal')),
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
];
}
public static function generateExportNotification() : Notification {
return Notification::make()
->title('Your export will be ready. check your notification for file download link.')
->success()
->send();
}
public static function exportTransmittal(array $id) : void {
$recipient = auth()->user();
static::generateExportNotification();
(new TransmittalsExport([$id]))->store('public/transmittal-export.xlsx')->chain([
app(ExportCompleteJob::class, [ 'user' => $recipient])
]);
}
public static function getPages(): array
{
return [
'index' => Pages\ListTransmittals::route('/'),
'create' => Pages\CreateTransmittal::route('/create'),
'view' => Pages\ViewTransmittal::route('/{record}'),
'edit' => Pages\EditTransmittal::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace App\Filament\Resources\TransmittalResource\Pages;
use App\Exports\TransmittalsExport;
use App\Filament\Resources\TransmittalResource;
use App\Jobs\ExportCompleteJob;
use Filament\Actions;
use Filament\Actions\Action;
use Filament\Forms\Form;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\CreateRecord;
use Filament\Support\Exceptions\Halt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Throwable;
use Filament\Notifications\Actions\Action as NotificationAction;
class CreateTransmittal extends CreateRecord
{
protected static string $resource = TransmittalResource::class;
protected function getFormActions(): array
{
return [
$this->getCreateFormAction(),
$this->getCreateAnotherFormAction(),
Action::make('Create and Export')->action('saveAndExport')->color('success'),
$this->getCancelFormAction()
];
}
/**
* @throws Throwable
*/
public function saveAndExport(): void
{
$this->authorizeAccess();
try {
DB::beginTransaction();
$this->callHook('beforeValidate');
$data = $this->form->getState();
$this->callHook('afterValidate');
$data = $this->mutateFormDataBeforeCreate($data);
$this->callHook('beforeCreate');
$this->record = $this->handleRecordCreation($data);
$this->form->model($this->getRecord())->saveRelationships();
$this->callHook('afterCreate');
DB::commit();
} catch (Halt $exception) {
$exception->shouldRollbackDatabaseTransaction() ?
DB::rollBack() :
DB::commit();
return;
} catch (Throwable $exception) {
DB::rollBack();
throw $exception;
}
$this->rememberData();
TransmittalResource::exportTransmittal([$this->record->id]);
Notification::make()
->success()
->title('Transmittal Was Created Successfully!, Check your notification for file download link')
->send();
}
public function getCreatedNotificationMessage(): ?string
{
return 'Transmittal Was Created Successfully!'; // TODO: Change the autogenerated stub
}
protected function getRedirectUrl(): string
{
return $this->previousUrl ?? $this->getResource()::getUrl('index');
}
}

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\TransmittalResource\Pages;
use App\Filament\Resources\TransmittalResource;
use Filament\Actions;
use Filament\Resources\Pages\ViewRecord;
class ViewTransmittal extends ViewRecord
{
protected static string $resource = TransmittalResource::class;
protected function getHeaderActions(): array
{
return [
Actions\EditAction::make(),
];
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Admin\Resources\UserResource\Pages;
use App\Filament\Admin\Resources\UserResource\RelationManagers;
use App\Models\User;
use Filament\Forms;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
class UserResource extends Resource
{
protected static ?string $model = User::class;
protected static ?string $navigationIcon = 'heroicon-o-user-group';
protected static ?string $navigationGroup = 'Security Settings';
public static function form(Form $form): Form
{
return $form
->columns([
'default' => 2,
])
->schema([
TextInput::make('name')->required()->columnSpan(2),
TextInput::make('email')->required()->email()->columnSpan(fn () : int => $form->getOperation() === 'edit' ? 2 : 1),
TextInput::make('password')->required()->password()->hiddenOn('edit'),
Forms\Components\CheckboxList::make('roles')
->relationship('roles', 'name')
->searchable()
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name')->searchable(),
Tables\Columns\TextColumn::make('email')->searchable(),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make()->slideOver(),
Tables\Actions\DeleteAction::make()->requiresConfirmation()
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => \App\Filament\Resources\UserResource\Pages\ListUsers::route('/'),
];
}
}

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\UserResource\Pages;
use App\Filament\Resources\UserResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListUsers extends ListRecords
{
protected static string $resource = UserResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make()->icon('heroicon-o-user-plus')->slideOver(),
];
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\RedirectResponse;
class VerifyEmailController extends Controller
{
/**
* Mark the authenticated user's email address as verified.
*/
public function __invoke(EmailVerificationRequest $request): RedirectResponse
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
}
if ($request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Jobs;
use Filament\Notifications\Actions\Action as NotificationAction;
use Filament\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;
class ExportCompleteJob implements ShouldQueue
{
use Queueable;
/**
* Create a new job instance.
*/
public function __construct(private $user)
{
}
/**
* Execute the job.
*/
public function handle(): void
{
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(),
]
)
->sendToDatabase($this->user);
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Livewire\Actions;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
class Logout
{
/**
* Log the current user out of the application.
*/
public function __invoke(): void
{
Auth::guard('web')->logout();
Session::invalidate();
Session::regenerateToken();
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace App\Livewire\Forms;
use Illuminate\Auth\Events\Lockout;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\Validate;
use Livewire\Form;
class LoginForm extends Form
{
#[Validate('required|string|email')]
public string $email = '';
#[Validate('required|string')]
public string $password = '';
#[Validate('boolean')]
public bool $remember = false;
/**
* Attempt to authenticate the request's credentials.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function authenticate(): void
{
$this->ensureIsNotRateLimited();
if (! Auth::attempt($this->only(['email', 'password']), $this->remember)) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'form.email' => trans('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
}
/**
* Ensure the authentication request is not rate limited.
*/
protected function ensureIsNotRateLimited(): void
{
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
return;
}
event(new Lockout(request()));
$seconds = RateLimiter::availableIn($this->throttleKey());
throw ValidationException::withMessages([
'form.email' => trans('auth.throttle', [
'seconds' => $seconds,
'minutes' => ceil($seconds / 60),
]),
]);
}
/**
* Get the authentication rate limiting throttle key.
*/
protected function throttleKey(): string
{
return Str::transliterate(Str::lower($this->email).'|'.request()->ip());
}
}

24
app/Models/Account.php Normal file
View File

@@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Account extends Model
{
use HasFactory;
protected $guarded= [];
public function accountType() : BelongsTo
{
return $this->belongsTo(AccountType::class);
}
public function client() : BelongsTo
{
return $this->belongsTo(Client::class);
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class AccountType extends Model
{
use HasFactory;
protected $guarded = [];
/**
* Get all of the accounts for the AccountType
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function accounts(): HasMany
{
return $this->hasMany(Account::class);
}
}

29
app/Models/Balance.php Normal file
View File

@@ -0,0 +1,29 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Balance extends Model
{
use HasFactory;
protected $guarded = [];
public function account(): BelongsTo
{
return $this->belongsTo(Account::class);
}
/**
* Get the ledger that owns the Balance
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function ledger(): BelongsTo
{
return $this->belongsTo(Ledger::class);
}
}

64
app/Models/Branch.php Normal file
View File

@@ -0,0 +1,64 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Branch extends Model
{
use HasFactory;
protected $guarded = [];
/**
* Get the client that owns the Branch
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function client(): BelongsTo
{
return $this->belongsTo(Client::class);
}
/**
* Get all of the series for the Branch
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function series(): HasMany
{
return $this->hasMany(Series::class);
}
public function getCurrentSeriesAttribute()
{
if ($this->series()->count() > 0) {
return $this->series()->orderBy('id', 'desc')->first()->series;
}
return null;
}
/**
* Get all of the sales for the Branch
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function sales(): HasMany
{
return $this->hasMany(Sale::class);
}
/**
* Get all of the expenses for the Branch
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function expenses(): HasMany
{
return $this->hasMany(Expense::class);
}
}

70
app/Models/Client.php Normal file
View File

@@ -0,0 +1,70 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Client extends Model
{
use HasFactory;
protected $guarded = [];
protected $appends = ['vatable'];
public function getFullNameAttribute()
{
return $this->lname . ', ' . $this->fname . ' ' . $this->mname;
}
public function getVatableAttribute()
{
return $this->type->type == 'Vatable' ? true : false;
}
/**
* Get all of the branches for the Client
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function branches(): HasMany
{
return $this->hasMany(Branch::class);
}
/**
* Get the type associated with the Client
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function type(): BelongsTo
{
return $this->belongsTo(ClientType::class);
}
public function accounts(): HasMany
{
return $this->hasMany(Account::class);
}
/**
* The users that belong to the Client
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
public function transmittals() : HasMany
{
return $this->hasMany(Transmittal::class);
}
}

13
app/Models/ClientType.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ClientType extends Model
{
use HasFactory;
protected $table = 'types';
}

13
app/Models/Comment.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
use HasFactory;
protected $guarded = [];
}

11
app/Models/Expense.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Expense extends Model
{
use HasFactory;
}

30
app/Models/File.php Normal file
View File

@@ -0,0 +1,30 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class File extends Model
{
use HasFactory;
protected $guarded = [];
public function transmittal(): BelongsTo
{
return $this->belongsTo(Transmittal::class);
}
public function notes(): HasMany
{
return $this->hasMany(Comment::class);
}
public function remarks(): HasMany
{
return $this->hasMany(Remark::class);
}
}

33
app/Models/Journal.php Normal file
View File

@@ -0,0 +1,33 @@
<?php
namespace App\Models;
use App\Traits\HasPeriod;
use App\Traits\HasUser;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
class Journal extends Model
{
use HasFactory;
protected $guarded = [];
protected $casts = ['happened_on' => 'date'];
public function ledger(): HasOne
{
return $this->hasOne(Ledger::class);
}
public function scopeDateCreatedFilter(Builder $query, $date) : Builder
{
$dates = explode(' to ',$date);
if (isset($dates[1])) {
return $query->whereBetween('happened_on' ,[$dates[0], $dates[1]]);
}
return $query->where('happened_on', $dates[0]);
}
}

65
app/Models/Ledger.php Normal file
View File

@@ -0,0 +1,65 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Ledger extends Model
{
use HasFactory;
protected $guarded = [];
public function scopeIsTransaction($query)
{
return $query->whereNotNull('transaction_id');
}
public function scopeIsJournal($query)
{
return $query->whereNull('transaction_id');
}
public function account(): BelongsTo
{
return $this->belongsTo(Account::class);
}
public function journal(): BelongsTo
{
return $this->belongsTo(Journal::class);
}
public function transaction() : BelongsTo
{
return $this->belongsTo(Transaction::class);
}
public function scopeDateCreatedFilter(Builder $query, $date) : Builder
{
$dates = explode(' to ',$date);
if (isset($dates[1])) {
return $query->whereHas('transaction', function ($transaction) use ($dates) {
$transaction->whereBetween('happened_on' ,[$dates[0], $dates[1]]);
});
}
return $query->whereHas('transaction', function ($transaction) use ($dates) {
$transaction->where('created_at', $dates[0]);
});
}
/**
* Get all of the balances for the Ledger
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function balances(): HasMany
{
return $this->hasMany(Balance::class);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Spatie\Permission\Models\Permission;
class PermissionType extends Model
{
use HasFactory;
/**
* Get all of the permissions for the PermissionType
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function permissions(): HasMany
{
return $this->hasMany(Permission::class);
}
}

13
app/Models/Remark.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Remark extends Model
{
use HasFactory;
protected $guarded = [];
}

11
app/Models/Sale.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Sale extends Model
{
use HasFactory;
}

11
app/Models/SaleSeries.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class SaleSeries extends Model
{
use HasFactory;
}

19
app/Models/Series.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Series extends Model
{
use HasFactory;
protected $guarded = [];
// public function setSeriesAttribute($value)
// {
// $this->attributes['series'] = str_pad($value, 6, '0', STR_PAD_LEFT);
// }
}

View File

@@ -0,0 +1,55 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Transaction extends Model
{
use HasFactory;
protected $guarded = [];
/**
* Get the expense that owns the Transaction
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function expense(): BelongsTo
{
return $this->belongsTo(Expense::class);
}
/**
* Get the sale that owns the Transaction
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function sale(): BelongsTo
{
return $this->belongsTo(Sale::class);
}
/**
* Get the account that owns the Transaction
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function account(): BelongsTo
{
return $this->belongsTo(Account::class);
}
/**
* Get the ledgers associated with the Transaction
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function ledgers(): HasMany
{
return $this->hasMany(Ledger::class);
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Query\Builder;
use Laravel\Prompts\Note;
class Transmittal extends Model
{
use HasFactory;
protected $guarded = [];
protected $casts = [
'date_created' => 'date',
'date_dispatch' => 'date',
'date_received' => 'date'
];
public function files(): HasMany
{
return $this->hasMany(File::class);
}
public function notes(): HasManyThrough
{
return $this->hasManyThrough(Comment::class, File::class);
}
public function remarks(): HasManyThrough
{
return $this->hasManyThrough(Remark::class, File::class);
}
public function client(): BelongsTo
{
return $this->belongsTo(Client::class);
}
public function branch(): BelongsTo
{
return $this->belongsTo(Branch::class);
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function scopeDateCreatedFilter(Builder $query, $date) : Builder
{
$dates = explode(' to ',$date);
if(isset($dates[1])) {
return $query->whereBetween('date_created' ,[$dates[0], $dates[1]]);
}
return $query->where('date_created', $dates[0]);
}
public function scopeDateDispatchedFilter(Builder $query, $date) : Builder
{
$dates = explode(' to ',$date);
if(isset($dates[1])) {
return $query->whereBetween('date_dispatch', [$dates[0], $dates[1]]);
}
return $query->where('date_dispatch', $dates[0]);
}
public function scopeDateReceivedFilter(Builder $query, $date) : Builder
{
$dates = explode(' to ',$date);
if(isset($dates[1])) {
return $query->whereBetween('date_received', [$dates[0], $dates[1]]);
}
return $query->where('date_received', $dates[0]);
}
}

11
app/Models/Type.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Type extends Model
{
use HasFactory;
}

View File

@@ -3,13 +3,17 @@
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use BezhanSalleh\FilamentShield\Traits\HasPanelShield;
use Filament\Models\Contracts\FilamentUser;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Spatie\Permission\Traits\HasPermissions;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasFactory, Notifiable;
use HasFactory, Notifiable, HasRoles, HasPermissions;
/**
* The attributes that are mass assignable.

View File

@@ -0,0 +1,108 @@
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\Client;
use Illuminate\Auth\Access\HandlesAuthorization;
class ClientPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_client');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, Client $client): bool
{
return $user->can('view_client');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_client');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Client $client): bool
{
return $user->can('update_client');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Client $client): bool
{
return $user->can('delete_client');
}
/**
* Determine whether the user can bulk delete.
*/
public function deleteAny(User $user): bool
{
return $user->can('delete_any_client');
}
/**
* Determine whether the user can permanently delete.
*/
public function forceDelete(User $user, Client $client): bool
{
return $user->can('force_delete_client');
}
/**
* Determine whether the user can permanently bulk delete.
*/
public function forceDeleteAny(User $user): bool
{
return $user->can('force_delete_any_client');
}
/**
* Determine whether the user can restore.
*/
public function restore(User $user, Client $client): bool
{
return $user->can('restore_client');
}
/**
* Determine whether the user can bulk restore.
*/
public function restoreAny(User $user): bool
{
return $user->can('restore_any_client');
}
/**
* Determine whether the user can replicate.
*/
public function replicate(User $user, Client $client): bool
{
return $user->can('replicate_client');
}
/**
* Determine whether the user can reorder.
*/
public function reorder(User $user): bool
{
return $user->can('reorder_client');
}
}

108
app/Policies/RolePolicy.php Normal file
View File

@@ -0,0 +1,108 @@
<?php
namespace App\Policies;
use App\Models\User;
use Spatie\Permission\Models\Role;
use Illuminate\Auth\Access\HandlesAuthorization;
class RolePolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_shield::role');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, Role $role): bool
{
return $user->can('view_shield::role');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_shield::role');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Role $role): bool
{
return $user->can('update_shield::role');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Role $role): bool
{
return $user->can('delete_shield::role');
}
/**
* Determine whether the user can bulk delete.
*/
public function deleteAny(User $user): bool
{
return $user->can('delete_any_shield::role');
}
/**
* Determine whether the user can permanently delete.
*/
public function forceDelete(User $user, Role $role): bool
{
return $user->can('{{ ForceDelete }}');
}
/**
* Determine whether the user can permanently bulk delete.
*/
public function forceDeleteAny(User $user): bool
{
return $user->can('{{ ForceDeleteAny }}');
}
/**
* Determine whether the user can restore.
*/
public function restore(User $user, Role $role): bool
{
return $user->can('{{ Restore }}');
}
/**
* Determine whether the user can bulk restore.
*/
public function restoreAny(User $user): bool
{
return $user->can('{{ RestoreAny }}');
}
/**
* Determine whether the user can replicate.
*/
public function replicate(User $user, Role $role): bool
{
return $user->can('{{ Replicate }}');
}
/**
* Determine whether the user can reorder.
*/
public function reorder(User $user): bool
{
return $user->can('{{ Reorder }}');
}
}

View File

@@ -0,0 +1,108 @@
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\Transmittal;
use Illuminate\Auth\Access\HandlesAuthorization;
class TransmittalPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_transmittal');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, Transmittal $transmittal): bool
{
return $user->can('view_transmittal');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_transmittal');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Transmittal $transmittal): bool
{
return $user->can('update_transmittal');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Transmittal $transmittal): bool
{
return $user->can('delete_transmittal');
}
/**
* Determine whether the user can bulk delete.
*/
public function deleteAny(User $user): bool
{
return $user->can('delete_any_transmittal');
}
/**
* Determine whether the user can permanently delete.
*/
public function forceDelete(User $user, Transmittal $transmittal): bool
{
return $user->can('force_delete_transmittal');
}
/**
* Determine whether the user can permanently bulk delete.
*/
public function forceDeleteAny(User $user): bool
{
return $user->can('force_delete_any_transmittal');
}
/**
* Determine whether the user can restore.
*/
public function restore(User $user, Transmittal $transmittal): bool
{
return $user->can('restore_transmittal');
}
/**
* Determine whether the user can bulk restore.
*/
public function restoreAny(User $user): bool
{
return $user->can('restore_any_transmittal');
}
/**
* Determine whether the user can replicate.
*/
public function replicate(User $user, Transmittal $transmittal): bool
{
return $user->can('replicate_transmittal');
}
/**
* Determine whether the user can reorder.
*/
public function reorder(User $user): bool
{
return $user->can('reorder_transmittal');
}
}

144
app/Policies/UserPolicy.php Normal file
View File

@@ -0,0 +1,144 @@
<?php
namespace App\Policies;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class UserPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*
* @param \App\Models\User $user
* @return bool
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_user');
}
/**
* Determine whether the user can view the model.
*
* @param \App\Models\User $user
* @return bool
*/
public function view(User $user): bool
{
return $user->can('view_user');
}
/**
* Determine whether the user can create models.
*
* @param \App\Models\User $user
* @return bool
*/
public function create(User $user): bool
{
return $user->can('create_user');
}
/**
* Determine whether the user can update the model.
*
* @param \App\Models\User $user
* @return bool
*/
public function update(User $user): bool
{
return $user->can('update_user');
}
/**
* Determine whether the user can delete the model.
*
* @param \App\Models\User $user
* @return bool
*/
public function delete(User $user): bool
{
return $user->can('delete_user');
}
/**
* Determine whether the user can bulk delete.
*
* @param \App\Models\User $user
* @return bool
*/
public function deleteAny(User $user): bool
{
return $user->can('delete_any_user');
}
/**
* Determine whether the user can permanently delete.
*
* @param \App\Models\User $user
* @return bool
*/
public function forceDelete(User $user): bool
{
return $user->can('force_delete_user');
}
/**
* Determine whether the user can permanently bulk delete.
*
* @param \App\Models\User $user
* @return bool
*/
public function forceDeleteAny(User $user): bool
{
return $user->can('force_delete_any_user');
}
/**
* Determine whether the user can restore.
*
* @param \App\Models\User $user
* @return bool
*/
public function restore(User $user): bool
{
return $user->can('restore_user');
}
/**
* Determine whether the user can bulk restore.
*
* @param \App\Models\User $user
* @return bool
*/
public function restoreAny(User $user): bool
{
return $user->can('restore_any_user');
}
/**
* Determine whether the user can bulk restore.
*
* @param \App\Models\User $user
* @return bool
*/
public function replicate(User $user): bool
{
return $user->can('replicate_user');
}
/**
* Determine whether the user can reorder.
*
* @param \App\Models\User $user
* @return bool
*/
public function reorder(User $user): bool
{
return $user->can('reorder_user');
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Processes;
use Illuminate\Support\Facades\Pipeline;
abstract class BaseProcess
{
/**
* @var array<class-string>
*
*/
protected array $tasks = [];
public function run(mixed $payload): mixed
{
return Pipeline::send(
passable: $payload,
)->through(
pipes: $this->tasks,
)->thenReturn();
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Processes\Branch;
use App\Actions\Branch\StoreBranch;
use App\Actions\Branch\StoreBranchSeries;
use App\Processes\BaseProcess;
class CreateBranchProcess extends BaseProcess
{
protected array $tasks = [
StoreBranch::class,
StoreBranchSeries::class
];
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Processes\Transmittal;
use App\Actions\Transmittal\CreateTransmittal;
use App\Actions\Transmittal\CreateTransmittalFiles;
use App\Processes\BaseProcess;
class CreateTransmittalProcess extends BaseProcess
{
public array $tasks = [
CreateTransmittal::class,
CreateTransmittalFiles::class
];
}

View File

@@ -0,0 +1,78 @@
<?php
namespace App\Providers\Filament;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Pages;
use Filament\Panel;
use Filament\PanelProvider;
use Filament\Support\Colors\Color;
use Filament\Widgets;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
->login()
->colors([
'primary' => Color::Amber,
])
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
->pages([
Pages\Dashboard::class,
])
->databaseNotifications()
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->widgets([
Widgets\AccountWidget::class,
])
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
])
->authMiddleware([
Authenticate::class,
])
->plugins([
\BezhanSalleh\FilamentShield\FilamentShieldPlugin::make()
->gridColumns([
'default' => 1,
'sm' => 2,
'lg' => 3
])
->sectionColumnSpan(1)
->checkboxListColumns([
'default' => 1,
'sm' => 2,
'lg' => 4,
])
->resourceCheckboxListColumns([
'default' => 1,
'sm' => 2,
]),
])
->viteTheme('resources/css/filament/admin/theme.css')
;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Livewire\Volt\Volt;
class VoltServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
//
}
/**
* Bootstrap services.
*/
public function boot(): void
{
Volt::mount([
config('livewire.view_path', resource_path('views/livewire')),
resource_path('views/pages'),
]);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class AppLayout extends Component
{
/**
* Get the view / contents that represents the component.
*/
public function render(): View
{
return view('layouts.app');
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class GuestLayout extends Component
{
/**
* Get the view / contents that represents the component.
*/
public function render(): View
{
return view('layouts.guest');
}
}

View File

@@ -2,4 +2,6 @@
return [
App\Providers\AppServiceProvider::class,
App\Providers\Filament\AdminPanelProvider::class,
App\Providers\VoltServiceProvider::class,
];

View File

@@ -2,21 +2,34 @@
"name": "laravel/laravel",
"type": "project",
"description": "The skeleton application for the Laravel framework.",
"keywords": ["laravel", "framework"],
"keywords": [
"laravel",
"framework"
],
"license": "MIT",
"require": {
"php": "^8.2",
"bezhansalleh/filament-shield": "^3.2",
"filament/filament": "^3.2",
"laravel/framework": "^11.9",
"laravel/tinker": "^2.9"
"laravel/tinker": "^2.9",
"livewire/livewire": "^3.4",
"livewire/volt": "^1.0",
"maatwebsite/excel": "^3.1",
"malzariey/filament-daterangepicker-filter": "^2.8",
"pxlrbt/filament-excel": "^2.3",
"spatie/laravel-data": "^4.7",
"yemenopensource/filament-excel": "*"
},
"require-dev": {
"fakerphp/faker": "^1.23",
"laravel/breeze": "^2.1",
"laravel/pint": "^1.13",
"laravel/sail": "^1.26",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.0",
"pestphp/pest": "^2.34",
"pestphp/pest-plugin-laravel": "^2.4"
"pestphp/pest": "^2.0",
"pestphp/pest-plugin-laravel": "^2.0"
},
"autoload": {
"psr-4": {
@@ -33,7 +46,8 @@
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
"@php artisan package:discover --ansi",
"@php artisan filament:upgrade"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"

4243
composer.lock generated

File diff suppressed because it is too large Load Diff

379
config/excel.php Normal file
View File

@@ -0,0 +1,379 @@
<?php
use Maatwebsite\Excel\Excel;
return [
'exports' => [
/*
|--------------------------------------------------------------------------
| Chunk size
|--------------------------------------------------------------------------
|
| When using FromQuery, the query is automatically chunked.
| Here you can specify how big the chunk should be.
|
*/
'chunk_size' => 1000,
/*
|--------------------------------------------------------------------------
| Pre-calculate formulas during export
|--------------------------------------------------------------------------
*/
'pre_calculate_formulas' => false,
/*
|--------------------------------------------------------------------------
| Enable strict null comparison
|--------------------------------------------------------------------------
|
| When enabling strict null comparison empty cells ('') will
| be added to the sheet.
*/
'strict_null_comparison' => false,
/*
|--------------------------------------------------------------------------
| CSV Settings
|--------------------------------------------------------------------------
|
| Configure e.g. delimiter, enclosure and line ending for CSV exports.
|
*/
'csv' => [
'delimiter' => ',',
'enclosure' => '"',
'line_ending' => PHP_EOL,
'use_bom' => false,
'include_separator_line' => false,
'excel_compatibility' => false,
'output_encoding' => '',
'test_auto_detect' => true,
],
/*
|--------------------------------------------------------------------------
| Worksheet properties
|--------------------------------------------------------------------------
|
| Configure e.g. default title, creator, subject,...
|
*/
'properties' => [
'creator' => '',
'lastModifiedBy' => '',
'title' => '',
'description' => '',
'subject' => '',
'keywords' => '',
'category' => '',
'manager' => '',
'company' => '',
],
],
'imports' => [
/*
|--------------------------------------------------------------------------
| Read Only
|--------------------------------------------------------------------------
|
| When dealing with imports, you might only be interested in the
| data that the sheet exists. By default we ignore all styles,
| however if you want to do some logic based on style data
| you can enable it by setting read_only to false.
|
*/
'read_only' => true,
/*
|--------------------------------------------------------------------------
| Ignore Empty
|--------------------------------------------------------------------------
|
| When dealing with imports, you might be interested in ignoring
| rows that have null values or empty strings. By default rows
| containing empty strings or empty values are not ignored but can be
| ignored by enabling the setting ignore_empty to true.
|
*/
'ignore_empty' => false,
/*
|--------------------------------------------------------------------------
| Heading Row Formatter
|--------------------------------------------------------------------------
|
| Configure the heading row formatter.
| Available options: none|slug|custom
|
*/
'heading_row' => [
'formatter' => 'slug',
],
/*
|--------------------------------------------------------------------------
| CSV Settings
|--------------------------------------------------------------------------
|
| Configure e.g. delimiter, enclosure and line ending for CSV imports.
|
*/
'csv' => [
'delimiter' => null,
'enclosure' => '"',
'escape_character' => '\\',
'contiguous' => false,
'input_encoding' => 'UTF-8',
],
/*
|--------------------------------------------------------------------------
| Worksheet properties
|--------------------------------------------------------------------------
|
| Configure e.g. default title, creator, subject,...
|
*/
'properties' => [
'creator' => '',
'lastModifiedBy' => '',
'title' => '',
'description' => '',
'subject' => '',
'keywords' => '',
'category' => '',
'manager' => '',
'company' => '',
],
/*
|--------------------------------------------------------------------------
| Cell Middleware
|--------------------------------------------------------------------------
|
| Configure middleware that is executed on getting a cell value
|
*/
'cells' => [
'middleware' => [
//\Maatwebsite\Excel\Middleware\TrimCellValue::class,
//\Maatwebsite\Excel\Middleware\ConvertEmptyCellValuesToNull::class,
],
],
],
/*
|--------------------------------------------------------------------------
| Extension detector
|--------------------------------------------------------------------------
|
| Configure here which writer/reader type should be used when the package
| needs to guess the correct type based on the extension alone.
|
*/
'extension_detector' => [
'xlsx' => Excel::XLSX,
'xlsm' => Excel::XLSX,
'xltx' => Excel::XLSX,
'xltm' => Excel::XLSX,
'xls' => Excel::XLS,
'xlt' => Excel::XLS,
'ods' => Excel::ODS,
'ots' => Excel::ODS,
'slk' => Excel::SLK,
'xml' => Excel::XML,
'gnumeric' => Excel::GNUMERIC,
'htm' => Excel::HTML,
'html' => Excel::HTML,
'csv' => Excel::CSV,
'tsv' => Excel::TSV,
/*
|--------------------------------------------------------------------------
| PDF Extension
|--------------------------------------------------------------------------
|
| Configure here which Pdf driver should be used by default.
| Available options: Excel::MPDF | Excel::TCPDF | Excel::DOMPDF
|
*/
'pdf' => Excel::DOMPDF,
],
/*
|--------------------------------------------------------------------------
| Value Binder
|--------------------------------------------------------------------------
|
| PhpSpreadsheet offers a way to hook into the process of a value being
| written to a cell. In there some assumptions are made on how the
| value should be formatted. If you want to change those defaults,
| you can implement your own default value binder.
|
| Possible value binders:
|
| [x] Maatwebsite\Excel\DefaultValueBinder::class
| [x] PhpOffice\PhpSpreadsheet\Cell\StringValueBinder::class
| [x] PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder::class
|
*/
'value_binder' => [
'default' => Maatwebsite\Excel\DefaultValueBinder::class,
],
'cache' => [
/*
|--------------------------------------------------------------------------
| Default cell caching driver
|--------------------------------------------------------------------------
|
| By default PhpSpreadsheet keeps all cell values in memory, however when
| dealing with large files, this might result into memory issues. If you
| want to mitigate that, you can configure a cell caching driver here.
| When using the illuminate driver, it will store each value in the
| cache store. This can slow down the process, because it needs to
| store each value. You can use the "batch" store if you want to
| only persist to the store when the memory limit is reached.
|
| Drivers: memory|illuminate|batch
|
*/
'driver' => 'memory',
/*
|--------------------------------------------------------------------------
| Batch memory caching
|--------------------------------------------------------------------------
|
| When dealing with the "batch" caching driver, it will only
| persist to the store when the memory limit is reached.
| Here you can tweak the memory limit to your liking.
|
*/
'batch' => [
'memory_limit' => 60000,
],
/*
|--------------------------------------------------------------------------
| Illuminate cache
|--------------------------------------------------------------------------
|
| When using the "illuminate" caching driver, it will automatically use
| your default cache store. However if you prefer to have the cell
| cache on a separate store, you can configure the store name here.
| You can use any store defined in your cache config. When leaving
| at "null" it will use the default store.
|
*/
'illuminate' => [
'store' => null,
],
/*
|--------------------------------------------------------------------------
| Cache Time-to-live (TTL)
|--------------------------------------------------------------------------
|
| The TTL of items written to cache. If you want to keep the items cached
| indefinitely, set this to null. Otherwise, set a number of seconds,
| a \DateInterval, or a callable.
|
| Allowable types: callable|\DateInterval|int|null
|
*/
'default_ttl' => 10800,
],
/*
|--------------------------------------------------------------------------
| Transaction Handler
|--------------------------------------------------------------------------
|
| By default the import is wrapped in a transaction. This is useful
| for when an import may fail and you want to retry it. With the
| transactions, the previous import gets rolled-back.
|
| You can disable the transaction handler by setting this to null.
| Or you can choose a custom made transaction handler here.
|
| Supported handlers: null|db
|
*/
'transactions' => [
'handler' => 'db',
'db' => [
'connection' => null,
],
],
'temporary_files' => [
/*
|--------------------------------------------------------------------------
| Local Temporary Path
|--------------------------------------------------------------------------
|
| When exporting and importing files, we use a temporary file, before
| storing reading or downloading. Here you can customize that path.
| permissions is an array with the permission flags for the directory (dir)
| and the create file (file).
|
*/
'local_path' => storage_path('framework/cache/laravel-excel'),
/*
|--------------------------------------------------------------------------
| Local Temporary Path Permissions
|--------------------------------------------------------------------------
|
| Permissions is an array with the permission flags for the directory (dir)
| and the create file (file).
| If omitted the default permissions of the filesystem will be used.
|
*/
'local_permissions' => [
// 'dir' => 0755,
// 'file' => 0644,
],
/*
|--------------------------------------------------------------------------
| Remote Temporary Disk
|--------------------------------------------------------------------------
|
| When dealing with a multi server setup with queues in which you
| cannot rely on having a shared local temporary path, you might
| want to store the temporary file on a shared disk. During the
| queue executing, we'll retrieve the temporary file from that
| location instead. When left to null, it will always use
| the local path. This setting only has effect when using
| in conjunction with queued imports and exports.
|
*/
'remote_disk' => null,
'remote_prefix' => null,
/*
|--------------------------------------------------------------------------
| Force Resync
|--------------------------------------------------------------------------
|
| When dealing with a multi server setup as above, it's possible
| for the clean up that occurs after entire queue has been run to only
| cleanup the server that the last AfterImportJob runs on. The rest of the server
| would still have the local temporary file stored on it. In this case your
| local storage limits can be exceeded and future imports won't be processed.
| To mitigate this you can set this config value to be true, so that after every
| queued chunk is processed the local temporary file is deleted on the server that
| processed it.
|
*/
'force_resync_remote' => null,
],
];

View File

@@ -0,0 +1,89 @@
<?php
return [
'shield_resource' => [
'should_register_navigation' => true,
'slug' => 'shield/roles',
'navigation_sort' => -1,
'navigation_badge' => true,
'navigation_group' => true,
'is_globally_searchable' => true,
'show_model_path' => true,
'is_scoped_to_tenant' => true,
'cluster' => null,
],
'auth_provider_model' => [
'fqcn' => 'App\\Models\\User',
],
'super_admin' => [
'enabled' => true,
'name' => 'super_admin',
'define_via_gate' => false,
'intercept_gate' => 'before', // after
],
'panel_user' => [
'enabled' => true,
'name' => 'panel_user',
],
'permission_prefixes' => [
'resource' => [
'view',
'view_any',
'create',
'update',
'restore',
'restore_any',
'replicate',
'reorder',
'delete',
'delete_any',
'force_delete',
'force_delete_any',
],
'page' => 'page',
'widget' => 'widget',
],
'entities' => [
'pages' => true,
'widgets' => true,
'resources' => true,
'custom_permissions' => true,
],
'generator' => [
'option' => 'policies_and_permissions',
'policy_directory' => 'Policies',
'policy_namespace' => 'Policies',
],
'exclude' => [
'enabled' => true,
'pages' => [
'Dashboard',
],
'widgets' => [
'AccountWidget', 'FilamentInfoWidget',
],
'resources' => [],
],
'discovery' => [
'discover_all_resources' => true,
'discover_all_widgets' => true,
'discover_all_pages' => true,
],
'register_role_policy' => [
'enabled' => true,
],
];

186
config/permission.php Normal file
View File

@@ -0,0 +1,186 @@
<?php
return [
'models' => [
/*
* When using the "HasPermissions" trait from this package, we need to know which
* Eloquent model should be used to retrieve your permissions. Of course, it
* is often just the "Permission" model but you may use whatever you like.
*
* The model you want to use as a Permission model needs to implement the
* `Spatie\Permission\Contracts\Permission` contract.
*/
'permission' => Spatie\Permission\Models\Permission::class,
/*
* When using the "HasRoles" trait from this package, we need to know which
* Eloquent model should be used to retrieve your roles. Of course, it
* is often just the "Role" model but you may use whatever you like.
*
* The model you want to use as a Role model needs to implement the
* `Spatie\Permission\Contracts\Role` contract.
*/
'role' => Spatie\Permission\Models\Role::class,
],
'table_names' => [
/*
* When using the "HasRoles" trait from this package, we need to know which
* table should be used to retrieve your roles. We have chosen a basic
* default value but you may easily change it to any table you like.
*/
'roles' => 'roles',
/*
* When using the "HasPermissions" trait from this package, we need to know which
* table should be used to retrieve your permissions. We have chosen a basic
* default value but you may easily change it to any table you like.
*/
'permissions' => 'permissions',
/*
* When using the "HasPermissions" trait from this package, we need to know which
* table should be used to retrieve your models permissions. We have chosen a
* basic default value but you may easily change it to any table you like.
*/
'model_has_permissions' => 'model_has_permissions',
/*
* When using the "HasRoles" trait from this package, we need to know which
* table should be used to retrieve your models roles. We have chosen a
* basic default value but you may easily change it to any table you like.
*/
'model_has_roles' => 'model_has_roles',
/*
* When using the "HasRoles" trait from this package, we need to know which
* table should be used to retrieve your roles permissions. We have chosen a
* basic default value but you may easily change it to any table you like.
*/
'role_has_permissions' => 'role_has_permissions',
],
'column_names' => [
/*
* Change this if you want to name the related pivots other than defaults
*/
'role_pivot_key' => null, //default 'role_id',
'permission_pivot_key' => null, //default 'permission_id',
/*
* Change this if you want to name the related model primary key other than
* `model_id`.
*
* For example, this would be nice if your primary keys are all UUIDs. In
* that case, name this `model_uuid`.
*/
'model_morph_key' => 'model_id',
/*
* Change this if you want to use the teams feature and your related model's
* foreign key is other than `team_id`.
*/
'team_foreign_key' => 'team_id',
],
/*
* When set to true, the method for checking permissions will be registered on the gate.
* Set this to false if you want to implement custom logic for checking permissions.
*/
'register_permission_check_method' => true,
/*
* When set to true, Laravel\Octane\Events\OperationTerminated event listener will be registered
* this will refresh permissions on every TickTerminated, TaskTerminated and RequestTerminated
* NOTE: This should not be needed in most cases, but an Octane/Vapor combination benefited from it.
*/
'register_octane_reset_listener' => false,
/*
* Teams Feature.
* When set to true the package implements teams using the 'team_foreign_key'.
* If you want the migrations to register the 'team_foreign_key', you must
* set this to true before doing the migration.
* If you already did the migration then you must make a new migration to also
* add 'team_foreign_key' to 'roles', 'model_has_roles', and 'model_has_permissions'
* (view the latest version of this package's migration file)
*/
'teams' => false,
/*
* Passport Client Credentials Grant
* When set to true the package will use Passports Client to check permissions
*/
'use_passport_client_credentials' => false,
/*
* When set to true, the required permission names are added to exception messages.
* This could be considered an information leak in some contexts, so the default
* setting is false here for optimum safety.
*/
'display_permission_in_exception' => false,
/*
* When set to true, the required role names are added to exception messages.
* This could be considered an information leak in some contexts, so the default
* setting is false here for optimum safety.
*/
'display_role_in_exception' => false,
/*
* By default wildcard permission lookups are disabled.
* See documentation to understand supported syntax.
*/
'enable_wildcard_permission' => false,
/*
* The class to use for interpreting wildcard permissions.
* If you need to modify delimiters, override the class and specify its name here.
*/
// 'permission.wildcard_permission' => Spatie\Permission\WildcardPermission::class,
/* Cache-specific settings */
'cache' => [
/*
* By default all permissions are cached for 24 hours to speed up performance.
* When permissions or roles are updated the cache is flushed automatically.
*/
'expiration_time' => \DateInterval::createFromDateString('24 hours'),
/*
* The cache key used to store all permissions.
*/
'key' => 'spatie.permission.cache',
/*
* You may optionally indicate a specific cache driver to use for permission and
* role caching using any of the `store` drivers listed in the cache.php config
* file. Using 'default' here means to use the `default` set in cache.php.
*/
'store' => 'default',
],
];

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.
*
* @return void
*/
public function up()
{
Schema::create('types', function (Blueprint $table) {
$table->id();
$table->string('type');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('types_tables');
}
};

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('clients', function (Blueprint $table) {
$table->id();
$table->string('firstname');
$table->string('lastname');
$table->string('middlename')->nullable();
$table->string('company');
$table->foreignId('type_id')->constrained();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('clients');
}
};

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('branches', function (Blueprint $table) {
$table->id();
$table->string('code');
$table->foreignId('client_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('branches');
}
};

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.
*
* @return void
*/
public function up()
{
Schema::create('account_types', function (Blueprint $table) {
$table->id();
$table->string('type');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('account_types');
}
};

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 {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('accounts', function (Blueprint $table) {
$table->id();
$table->string('account');
$table->string('description')->nullable();
$table->foreignId('account_type_id')->constrained();
$table->string('normal_balance')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('accounts');
}
};

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 {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('sales', function (Blueprint $table) {
$table->id();
$table->string('reference_number');
$table->date('happened_on');
$table->string('buyer')->nullable();
$table->foreignId('branch_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('sales');
}
};

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 {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('series', function (Blueprint $table) {
$table->id();
$table->string('series');
$table->foreignId('branch_id')->constrained()->onDelete('cascade');
$table->foreignId('sale_id')->nullable()->constrained()->onDelete('cascade');
$table->boolean('is_start')->default(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('series');
}
};

View File

@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateExpensesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('expenses', function (Blueprint $table) {
$table->id();
$table->string('voucher_number');
$table->string('reference_number');
$table->date('happened_on');
$table->string('supplier')->nullable();
$table->foreignId('branch_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('expenses');
}
}

View File

@@ -0,0 +1,51 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('transactions', function (Blueprint $table) {
$table->id();
$table->foreignId('account_id')->constrained()->onDelete('cascade');
$table->string('description')->nullable();
$table->decimal('gross_amount');
$table->decimal('exempt')->nullable();
$table->decimal('zero_rated')->nullable();
$table->decimal('vatable_amount')->nullable();
$table->decimal('net_amount')->nullable();
$table->decimal('input_tax')->nullable();
$table->decimal('output_tax')->nullable();
$table->boolean('with_tax')->default(false);
$table->decimal('payable_withholding_tax')->nullable();
$table->decimal('creditable_withholding_tax')->nullable();
$table->foreignId('expense_id')->nullable()->constrained()->onDelete('cascade');
$table->foreignId('sale_id')->nullable()->constrained()->onDelete('cascade');
$table->foreignId('branch_id')->constrained()->onDelete('cascade');
$table->foreignId('client_id')->constrained()->onDelete('cascade');
$table->date('happened_on');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('transactions');
}
};

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 {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('journals', function (Blueprint $table) {
$table->id();
$table->longText('description')->nullable();
$table->date('happened_on')->nullable();
$table->foreignId('branch_id')->constrained()->onDelete('cascade');
$table->foreignId('client_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('journals');
}
};

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