Erster Upload
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources;
|
||||
|
||||
use App\Filament\User\Resources\BatchResource\Pages;
|
||||
use App\Models\Batch;
|
||||
use App\Models\Recipe;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class BatchResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Batch::class;
|
||||
protected static ?string $navigationIcon = 'heroicon-o-qr-code';
|
||||
protected static ?string $navigationLabel = 'Chargen (LOT)';
|
||||
protected static ?string $modelLabel = 'Charge';
|
||||
protected static ?string $pluralModelLabel = 'Chargen';
|
||||
protected static ?int $navigationSort = 5;
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form->schema([
|
||||
Forms\Components\Section::make('Charge')->schema([
|
||||
Forms\Components\TextInput::make('lot_number')
|
||||
->label('LOT-Nummer')
|
||||
->required()
|
||||
->unique(ignoreRecord: true)
|
||||
->maxLength(255),
|
||||
|
||||
Forms\Components\Select::make('product_id')
|
||||
->label('Produkt')
|
||||
->relationship('product', 'title')
|
||||
->searchable()
|
||||
->preload()
|
||||
->required()
|
||||
->live(),
|
||||
|
||||
Forms\Components\Select::make('recipe_id')
|
||||
->label('Rezeptur')
|
||||
->relationship('recipe', 'name')
|
||||
->searchable()
|
||||
->preload()
|
||||
->required()
|
||||
->live(),
|
||||
|
||||
Forms\Components\DatePicker::make('produced_at')
|
||||
->label('Produktionsdatum'),
|
||||
|
||||
Forms\Components\DatePicker::make('expires_at')
|
||||
->label('Mindesthaltbarkeitsdatum'),
|
||||
])->columns(2),
|
||||
|
||||
Forms\Components\Section::make('Herkunft der Gewürze')->schema([
|
||||
Forms\Components\Repeater::make('origins')
|
||||
->label('')
|
||||
->relationship()
|
||||
->schema([
|
||||
Forms\Components\Select::make('spice_id')
|
||||
->label('Gewürz')
|
||||
->relationship('spice', 'name')
|
||||
->searchable()
|
||||
->preload()
|
||||
->required(),
|
||||
|
||||
Forms\Components\Select::make('supplier_id')
|
||||
->label('Lieferant')
|
||||
->relationship('supplier', 'name')
|
||||
->searchable()
|
||||
->preload()
|
||||
->nullable(),
|
||||
|
||||
Forms\Components\TextInput::make('origin_country')
|
||||
->label('Herkunftsland')
|
||||
->maxLength(100),
|
||||
|
||||
Forms\Components\TextInput::make('farmer')
|
||||
->label('Landwirt / Betrieb')
|
||||
->maxLength(255),
|
||||
|
||||
Forms\Components\Textarea::make('notes')
|
||||
->label('Infos zum Gewürz')
|
||||
->rows(2)
|
||||
->columnSpanFull(),
|
||||
])
|
||||
->columns(2)
|
||||
->addActionLabel('Gewürz hinzufügen')
|
||||
->reorderable(false),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('lot_number')
|
||||
->label('LOT-Nummer')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('product.title')
|
||||
->label('Produkt')
|
||||
->searchable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('recipe.name')
|
||||
->label('Rezeptur'),
|
||||
|
||||
Tables\Columns\TextColumn::make('produced_at')
|
||||
->label('Produktion')
|
||||
->date('d.m.Y')
|
||||
->sortable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('expires_at')
|
||||
->label('MHD')
|
||||
->date('d.m.Y')
|
||||
->sortable(),
|
||||
])
|
||||
->defaultSort('produced_at', 'desc')
|
||||
->filters([
|
||||
Tables\Filters\SelectFilter::make('product')
|
||||
->relationship('product', 'title')
|
||||
->label('Produkt'),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
Tables\Actions\DeleteAction::make(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListBatches::route('/'),
|
||||
'create' => Pages\CreateBatch::route('/create'),
|
||||
'edit' => Pages\EditBatch::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\BatchResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\BatchResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateBatch extends CreateRecord
|
||||
{
|
||||
protected static string $resource = BatchResource::class;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\BatchResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\BatchResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditBatch extends EditRecord
|
||||
{
|
||||
protected static string $resource = BatchResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [Actions\DeleteAction::make()];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\BatchResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\BatchResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListBatches extends ListRecords
|
||||
{
|
||||
protected static string $resource = BatchResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [Actions\CreateAction::make()];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources;
|
||||
|
||||
use App\Filament\User\Resources\PackingResource\Pages;
|
||||
use App\Filament\User\Resources\PackingResource\RelationManagers;
|
||||
use App\Models\Packing;
|
||||
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 PackingResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Packing::class;
|
||||
protected static ?string $navgationLabel = 'Verpackung & Größe';
|
||||
protected static ?string $modelLabel = 'Verpackung & Größe';
|
||||
protected static ?string $slug = 'verpackung';
|
||||
protected static ?string $navigationIcon = 'heroicon-o-shopping-bag';
|
||||
protected static ?string $navigationGroup = 'Ressourcen';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('name')
|
||||
->required()
|
||||
->maxLength(255),
|
||||
Forms\Components\TextInput::make('size')
|
||||
->label('Größe')
|
||||
->required()
|
||||
->numeric(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('name')
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('size')
|
||||
->label('Größe')
|
||||
->numeric()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
Tables\Columns\TextColumn::make('updated_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->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\ListPackings::route('/'),
|
||||
'create' => Pages\CreatePacking::route('/create'),
|
||||
'edit' => Pages\EditPacking::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
public static function getPluralLabel(): string
|
||||
{
|
||||
return 'Verpackung & Größe'; // Setze hier deinen eigenen Pluralbegriff ein
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\PackingResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\PackingResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreatePacking extends CreateRecord
|
||||
{
|
||||
protected static string $resource = PackingResource::class;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\PackingResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\PackingResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditPacking extends EditRecord
|
||||
{
|
||||
protected static string $resource = PackingResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\PackingResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\PackingResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListPackings extends ListRecords
|
||||
{
|
||||
protected static string $resource = PackingResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources;
|
||||
|
||||
use App\Filament\User\Resources\ProductInventoryResource\Pages;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use App\Models\Product;
|
||||
use App\Models\ProductSize;
|
||||
use App\Models\ProductInventory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
|
||||
class ProductInventoryResource extends Resource
|
||||
{
|
||||
protected static ?string $model = ProductInventory::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-inbox';
|
||||
protected static ?string $navigationLabel = 'Inventar';
|
||||
protected static ?string $modelLabel = 'Inventar';
|
||||
protected static ?string $slug = 'inventar';
|
||||
protected static ?string $navigationGroup = 'Lager';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\Select::make('product_id')
|
||||
->label('Produkt')
|
||||
->native(false)
|
||||
->searchable()
|
||||
->options(Product::all()->pluck('title', 'id'))
|
||||
->reactive()
|
||||
->afterStateUpdated(fn(callable $set) => $set('size_id', null))
|
||||
->required()
|
||||
->disabled(fn($record) => !is_null($record)),
|
||||
|
||||
Forms\Components\Select::make('size_id')
|
||||
->label('Größe')
|
||||
->native(false)
|
||||
->options(function (callable $get) {
|
||||
$productId = $get('product_id');
|
||||
if (!$productId) {
|
||||
return [];
|
||||
}
|
||||
return ProductSize::where('product_id', $productId)
|
||||
->get()
|
||||
->mapWithKeys(function ($product) {
|
||||
return [$product->id => "{$product->description} ({$product->price}€)"];
|
||||
});
|
||||
})
|
||||
->required()
|
||||
->disabled(fn($record) => !is_null($record))
|
||||
->reactive(),
|
||||
Forms\Components\TextInput::make('amount')
|
||||
->label('Mange')
|
||||
->numeric(false)
|
||||
->step(1)
|
||||
->rules(['min:1'])
|
||||
->default(1)
|
||||
->required()
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('product.title')->label('Produkt'),
|
||||
Tables\Columns\TextColumn::make('size')->label('Größe')
|
||||
->formatStateUsing(fn(ProductInventory $record) => "{$record->size->description} ({$record->size->price}€)"),
|
||||
Tables\Columns\TextColumn::make('total')->label('Anzahl'),
|
||||
|
||||
|
||||
])
|
||||
->emptyStateHeading('Kein Inventar')
|
||||
->recordUrl(null);
|
||||
}
|
||||
|
||||
protected function getTableQuery(): Builder {
|
||||
return ProductInventory::select(
|
||||
DB::raw('MIN(id) as id'),
|
||||
DB::raw('product_id'),
|
||||
DB::raw('size_id'),
|
||||
DB::raw('count(*) as total')
|
||||
)
|
||||
->where()
|
||||
->groupBy('size_id');
|
||||
}
|
||||
|
||||
public static function getEloquentQuery(): Builder {
|
||||
return parent::getEloquentQuery()
|
||||
->whereNull('out_at')
|
||||
->select(DB::raw('MIN(id) AS id'), DB::raw('MIN(product_id) AS product_id'), 'size_id', DB::raw('COUNT(*) as total'))
|
||||
->groupBy('size_id')
|
||||
->orderBy('product_id');
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListProductInventory::route('/'),
|
||||
'create' => Pages\CreateProductInventory::route('/create'),
|
||||
'edit' => Pages\EditProductInventory::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPluralLabel(): string
|
||||
{
|
||||
return 'Inventar';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\ProductInventoryResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\ProductInventoryResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Filament\Notifications\Notification;
|
||||
use App\Models\Product;
|
||||
use App\Models\Spice;
|
||||
use App\Models\ProductSize;
|
||||
|
||||
class CreateProductInventory extends CreateRecord
|
||||
{
|
||||
protected static string $resource = ProductInventoryResource::class;
|
||||
|
||||
protected function handleRecordCreation(array $data): Model
|
||||
{
|
||||
$amount = $data['amount'] ?? 1;
|
||||
unset($data['amount']);
|
||||
|
||||
$product = Product::find($data['product_id']);
|
||||
if (!$product) {
|
||||
return false;
|
||||
}
|
||||
$size = ProductSize::find($data['size_id']);
|
||||
if (!$size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$recipe = $product->recipe;
|
||||
$packing = $size->packing;
|
||||
$totalAmount = $packing->size;
|
||||
|
||||
|
||||
foreach ($recipe->spices as $spice) {
|
||||
$percentage = $spice->pivot->amount_in_percent;
|
||||
$amountNeeded = (($percentage / 100) * $totalAmount) * $amount;
|
||||
|
||||
if (!$spice || $spice->quantity < $amountNeeded) {
|
||||
Notification::make()
|
||||
->warning()
|
||||
->title('Fehler!')
|
||||
->body('Nicht genügend '.htmlentities($spice->name).' für dieses Produkt in dieser Größe lagernd (bräuchte '.$amountNeeded.' '.htmlentities($spice->unit).')')
|
||||
->send();
|
||||
$this->halt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$records = [];
|
||||
for($i = 0; $i < $amount; $i++) {
|
||||
$records[] = static::getModel()::create($data);
|
||||
}
|
||||
|
||||
foreach ($recipe->spices as $spice) {
|
||||
$percentage = $spice->pivot->amount_in_percent;
|
||||
$amountNeeded = (($percentage / 100) * $totalAmount) * $amount;
|
||||
Spice::findOrFail($spice->id)->decrement('quantity', $amountNeeded);
|
||||
}
|
||||
|
||||
return $records[0];
|
||||
}
|
||||
|
||||
protected function getRedirectUrl(): string
|
||||
{
|
||||
return $this->previousUrl ?? $this->getResource()::getUrl('index');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\ProductInventoryResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\ProductInventoryResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditProductInventory extends EditRecord
|
||||
{
|
||||
protected static string $resource = ProductInventoryResource::class;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\ProductInventoryResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\ProductInventoryResource;
|
||||
use App\Models\ProductInventory;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ListProductInventory extends ListRecords
|
||||
{
|
||||
protected static string $resource = ProductInventoryResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources;
|
||||
|
||||
use App\Filament\User\Resources\ProductResource\Pages;
|
||||
use App\Filament\User\Resources\ProductResource\RelationManagers;
|
||||
use App\Models\Product;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Components\Repeater;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use App\Models\Packing;
|
||||
|
||||
class ProductResource extends Resource {
|
||||
protected static ?string $model = Product::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-inbox';
|
||||
protected static ?string $navigationLabel = 'Produkte';
|
||||
protected static ?string $modelLabel = 'Produkt';
|
||||
protected static ?string $slug = 'produkt';
|
||||
protected static ?string $navigationGroup = 'Ressourcen';
|
||||
|
||||
public static function form(Form $form): Form {
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('title')
|
||||
->label('Name')
|
||||
->required(),
|
||||
FileUpload::make('images')
|
||||
->label('Bilder')
|
||||
->multiple()
|
||||
->image()
|
||||
->maxFiles(15)
|
||||
->directory('product-images')
|
||||
->columnSpanFull(),
|
||||
Forms\Components\Textarea::make('description')
|
||||
->label('Beschreibung'),
|
||||
Forms\Components\Textarea::make('package_content')
|
||||
->label('Inhaltsangabe'),
|
||||
Forms\Components\Select::make('recipes_id')
|
||||
->label('Rezept')
|
||||
->native(false)
|
||||
->searchable()
|
||||
->relationship('recipe', 'name')
|
||||
->required(),
|
||||
Repeater::make('sizes')
|
||||
->schema([
|
||||
TextInput::make('gtin')
|
||||
->label('GTIN'),
|
||||
TextInput::make('description')
|
||||
->label('Bezeichnung'),
|
||||
Select::make('packing_id')
|
||||
->label('Verpackung')
|
||||
->options(function () {
|
||||
return Packing::all()->mapWithKeys(function ($packing) {
|
||||
return [$packing->id => $packing->name . ' (' . $packing->size . ')'];
|
||||
});
|
||||
})
|
||||
->searchable()
|
||||
->required(),
|
||||
TextInput::make('price')
|
||||
->label('Preis (in EUR)')
|
||||
->numeric()
|
||||
->required(),
|
||||
])
|
||||
->label('Größen & Preise')
|
||||
->columns(2)
|
||||
->addActionLabel('Option hinzufügen')
|
||||
->afterStateHydrated(function ($state, $set, $record) {
|
||||
if ($record) {
|
||||
// Load the spices relationship with pivot data
|
||||
$record->load('sizes');
|
||||
|
||||
// Map the spices and their pivot data to the format the repeater expects
|
||||
$sizesData = $record->sizes->map(function ($size) {
|
||||
return [
|
||||
'packing_id' => $size->id,
|
||||
'price' => $size->pivot->price,
|
||||
'description' => $size->pivot->description,
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
// Set the state for the repeater field to the mapped spices data
|
||||
$set('sizes', $sizesData);
|
||||
}
|
||||
return $state;
|
||||
}),
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table {
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\ImageColumn::make('images')
|
||||
->label('Bild')
|
||||
->limit(1),
|
||||
Tables\Columns\TextColumn::make('title')
|
||||
->label('Name')
|
||||
->sortable()
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('recipe.name')
|
||||
->label('Rezept'),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getPages(): array {
|
||||
return [
|
||||
'index' => Pages\ListProducts::route('/'),
|
||||
'create' => Pages\CreateProduct::route('/create'),
|
||||
'edit' => Pages\EditProduct::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPluralLabel(): string {
|
||||
return 'Produkte';
|
||||
}
|
||||
|
||||
|
||||
protected function mutateFormDataBeforeCreate(array $data): array {
|
||||
if (isset($data['images'])) {
|
||||
$data['images'] = json_encode($data['images']);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function mutateFormDataBeforeUpdate(array $data): array {
|
||||
if (isset($data['images'])) {
|
||||
$data['images'] = json_encode($data['images']);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
public static function getRecord($id) {
|
||||
return Product::with('sizes')->findOrFail($id); // Eager load spices relationship
|
||||
}
|
||||
|
||||
public static function getTableRecordTitle() {
|
||||
return function ($record) {
|
||||
$images = json_decode($record->images, true);
|
||||
return implode(', ', $images);
|
||||
};
|
||||
}
|
||||
|
||||
public static function getActions(): array {
|
||||
return [
|
||||
Actions\DeleteAction::make(),
|
||||
Action::make('deleteImage')
|
||||
->action(function ($record, $data) {
|
||||
$images = json_decode($record->images, true);
|
||||
$index = array_search($data['image'], $images);
|
||||
if ($index !== false) {
|
||||
unset($images[$index]);
|
||||
$record->update(['images' => array_values($images)]);
|
||||
}
|
||||
})
|
||||
->form([
|
||||
Forms\Components\Select::make('image')
|
||||
->options(function ($record) {
|
||||
return array_combine($record->images, $record->images);
|
||||
})
|
||||
->required(),
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\ProductResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\ProductResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Models\ProductSize;
|
||||
|
||||
class CreateProduct extends CreateRecord
|
||||
{
|
||||
protected static string $resource = ProductResource::class;
|
||||
|
||||
protected function handleRecordCreation(array $data): Model {
|
||||
|
||||
$record = static::getModel()::create($data);
|
||||
|
||||
foreach ($data['sizes'] as $size) {
|
||||
ProductSize::create([
|
||||
'product_id' => $record->id,
|
||||
'packing_id' => $size['packing_id'],
|
||||
'description' => $size['description'],
|
||||
'price' => $size['price'],
|
||||
]);
|
||||
}
|
||||
return $record;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\ProductResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\ProductResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditProduct extends EditRecord
|
||||
{
|
||||
protected static string $resource = ProductResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function afterSave(): void {
|
||||
$this->syncSizes();
|
||||
}
|
||||
|
||||
protected function syncSizes(): void {
|
||||
$product = $this->record;
|
||||
|
||||
$sizes = collect($this->data['sizes'] ?? [])
|
||||
->mapWithKeys(function ($size) {
|
||||
return [
|
||||
$size['packing_id'] => ['description' => $size['description'], 'price' =>$size['price']],
|
||||
];
|
||||
})
|
||||
->toArray();
|
||||
|
||||
$product->sizes()->sync($sizes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\ProductResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\ProductResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListProducts extends ListRecords
|
||||
{
|
||||
protected static string $resource = ProductResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources;
|
||||
|
||||
use App\Filament\User\Resources\ProductSaleResource\Pages;
|
||||
use App\Models\ProductSale;
|
||||
use App\Models\Product;
|
||||
use App\Models\ProductSize;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use App\Rules\ProductsHaveEnoughStock;
|
||||
|
||||
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
|
||||
class ProductSaleResource extends Resource {
|
||||
protected static ?string $model = ProductSale::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-arrow-up-on-square';
|
||||
|
||||
protected static ?string $navgationLabel = 'Warenausgang';
|
||||
protected static ?string $modelLabel = 'Warenausgang';
|
||||
protected static ?string $slug = 'warenausgang';
|
||||
protected static ?string $navigationGroup = 'Waren Ein/Ausgang';
|
||||
|
||||
|
||||
public $product_id;
|
||||
public $size_id;
|
||||
public $amount;
|
||||
|
||||
public static function form(Form $form): Form {
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\Select::make('product_id')
|
||||
->label('Produkt')
|
||||
->native(false)
|
||||
->searchable()
|
||||
->options(Product::all()->pluck('title', 'id'))
|
||||
->reactive()
|
||||
->afterStateUpdated(fn(callable $set) => $set('size_id', null))
|
||||
->disabled(fn($record) => !is_null($record))
|
||||
->required(),
|
||||
|
||||
Forms\Components\Select::make('size_id')
|
||||
->label('Größe')
|
||||
->native(false)
|
||||
->options(function (callable $get) {
|
||||
$productId = $get('product_id');
|
||||
if (!$productId) {
|
||||
return [];
|
||||
}
|
||||
return ProductSize::where('product_id', $productId)
|
||||
->get()
|
||||
->mapWithKeys(function ($product) {
|
||||
return [$product->id => "{$product->description} ({$product->price}€)"];
|
||||
});
|
||||
})
|
||||
->required()
|
||||
->disabled(fn($record) => !is_null($record))
|
||||
->reactive(),
|
||||
|
||||
TextInput::make('amount')
|
||||
->label('Verkaufsmenge')
|
||||
->numeric()
|
||||
->step(1)
|
||||
->required()
|
||||
->disabled(fn($record) => !is_null($record))
|
||||
->rules(['min:1']),
|
||||
|
||||
DatePicker::make('sold_at')
|
||||
->label('Verkaufsdatum')
|
||||
->default(now())
|
||||
->disabled(fn($record) => !is_null($record))
|
||||
->required(),
|
||||
|
||||
TextInput::make('comment')
|
||||
->label('Kommentar')
|
||||
->helperText('Verkaufsquelle (Bsp. Shopify, etc.)'),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table {
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('product.title')->label('Produkt'),
|
||||
Tables\Columns\TextColumn::make('size')->label('Größe')
|
||||
->formatStateUsing(fn(ProductSale $record) => "{$record->size->description} ({$record->size->price}€)"),
|
||||
TextColumn::make('amount')->label('Verkaufsmenge'),
|
||||
TextColumn::make('sold_at')->label('Verkaufsdatum')->date('d.m.Y'),
|
||||
TextColumn::make('comment')->label('Kommentar'),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
])
|
||||
->emptyStateHeading('Keine Warenausgänge')
|
||||
->recordUrl(null);
|
||||
}
|
||||
|
||||
public static function getRelations(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
public static function getPages(): array {
|
||||
return [
|
||||
'index' => Pages\ListProductSales::route('/'),
|
||||
'create' => Pages\CreateProductSale::route('/create'),
|
||||
'edit' => Pages\EditProductSale::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
public static function getPluralLabel(): string {
|
||||
return 'Warenausgang';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\ProductSaleResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\ProductSaleResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Filament\Notifications\Notification;
|
||||
|
||||
class CreateProductSale extends CreateRecord
|
||||
{
|
||||
protected static string $resource = ProductSaleResource::class;
|
||||
|
||||
protected function handleRecordCreation(array $data): Model
|
||||
{
|
||||
$count = DB::table('products_inventory')
|
||||
->where('product_id', '=', $data['product_id'])
|
||||
->where('size_id', '=', $data['size_id'])
|
||||
->count();
|
||||
|
||||
if ($count < $data['amount']) {
|
||||
Notification::make()
|
||||
->warning()
|
||||
->title('Fehler!')
|
||||
->body('Nicht genügend Produkte in dieser Größe lagernd.')
|
||||
->send();
|
||||
$this->halt();
|
||||
return false;
|
||||
}
|
||||
$record = static::getModel()::create($data);
|
||||
|
||||
DB::table('products_inventory')
|
||||
->where('product_id', '=', $data['product_id'])
|
||||
->where('size_id', '=', $data['size_id'])
|
||||
->limit($data['amount'] ?? 1)
|
||||
->update(['out_at' => now()]);
|
||||
|
||||
return $record;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\ProductSaleResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\ProductSaleResource;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditProductSale extends EditRecord
|
||||
{
|
||||
protected static string $resource = ProductSaleResource::class;
|
||||
|
||||
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
\Filament\Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\ProductSaleResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\ProductSaleResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListProductSales extends ListRecords
|
||||
{
|
||||
protected static string $resource = ProductSaleResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\ProductSaleResource\Widgets;
|
||||
|
||||
use Leandrocfe\FilamentApexCharts\Widgets\ApexChartWidget;
|
||||
use Flowframe\Trend\Trend;
|
||||
use Flowframe\Trend\TrendValue;
|
||||
use App\Models\ProductSale;
|
||||
|
||||
class SalesChart extends ApexChartWidget {
|
||||
protected static ?string $chartId = 'productSalesChart';
|
||||
protected static ?string $heading = 'Verkäufe dieses Quartal';
|
||||
|
||||
protected function getOptions(): array {
|
||||
$data = Trend::model(ProductSale::class)
|
||||
->between(
|
||||
start: now()->startOfQuarter(),
|
||||
end: now()->endOfQuarter(),
|
||||
)
|
||||
->perWeek()
|
||||
->count();
|
||||
|
||||
return [
|
||||
'chart' => [
|
||||
'type' => 'line',
|
||||
'height' => 300,
|
||||
],
|
||||
'series' => [
|
||||
[
|
||||
'name' => 'Anzahl Verkäufe',
|
||||
'data' => $data->map(fn(TrendValue $value) => $value->aggregate),
|
||||
],
|
||||
],
|
||||
'xaxis' => [
|
||||
'categories' => $data->map(fn(TrendValue $value) => $value->date),
|
||||
'labels' => [
|
||||
'style' => [
|
||||
'fontFamily' => 'inherit',
|
||||
],
|
||||
],
|
||||
],
|
||||
'yaxis' => [
|
||||
'labels' => [
|
||||
'style' => [
|
||||
'fontFamily' => 'inherit',
|
||||
],
|
||||
],
|
||||
],
|
||||
'colors' => ['#f59e0b'],
|
||||
'stroke' => [
|
||||
'curve' => 'smooth',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources;
|
||||
|
||||
use App\Filament\User\Resources\RecipeResource\Pages;
|
||||
use App\Models\Recipe;
|
||||
use App\Models\RecipeSpice;
|
||||
use App\Models\Spice;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\Repeater;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use App\Rules\SumEquals100;
|
||||
|
||||
class RecipeResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Recipe::class;
|
||||
protected static ?string $navigationIcon = 'heroicon-o-book-open';
|
||||
protected static ?string $navgationLabel = 'Rezepte';
|
||||
protected static ?string $modelLabel = 'Rezepte';
|
||||
protected static ?string $slug = 'rezept';
|
||||
protected static ?string $navigationGroup = 'Ressourcen';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
->required()
|
||||
->label('Name'),
|
||||
Textarea::make('description')
|
||||
->label('Beschreibung'),
|
||||
Repeater::make('spices')
|
||||
->schema([
|
||||
Select::make('spice_id')
|
||||
->label('Rohstoff')
|
||||
->options(Spice::all()->pluck('name', 'id'))
|
||||
->searchable()
|
||||
->required(),
|
||||
|
||||
TextInput::make('amount_in_percent')
|
||||
->label('Anteil (in Prozent)')
|
||||
->numeric()
|
||||
->required()
|
||||
->rules(['min:1', 'max:100']),
|
||||
])
|
||||
->label('Inhalt')
|
||||
->columns(2)
|
||||
->addActionLabel('Inhaltsstoff hinzufügen')
|
||||
->afterStateHydrated(function ($state, $set, $record) {
|
||||
if ($record) {
|
||||
// Load the spices relationship with pivot data
|
||||
$record->load('spices');
|
||||
|
||||
// Map the spices and their pivot data to the format the repeater expects
|
||||
$spicesData = $record->spices->map(function ($spice) {
|
||||
return [
|
||||
'spice_id' => $spice->id,
|
||||
'amount_in_percent' => $spice->pivot->amount_in_percent,
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
// Set the state for the repeater field to the mapped spices data
|
||||
$set('spices', $spicesData);
|
||||
}
|
||||
return $state;
|
||||
})
|
||||
->rules([new SumEquals100()]),
|
||||
]);
|
||||
}
|
||||
/*
|
||||
*/
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('name')
|
||||
->label('Name')
|
||||
->sortable()
|
||||
->searchable(),
|
||||
TextColumn::make('description')
|
||||
->label('Beschreibung')
|
||||
->limit(50),
|
||||
TextColumn::make('spices.name')
|
||||
->label('Inhalt')
|
||||
->formatStateUsing(function ($state, $record) {
|
||||
$spices = $record->spices->take(3)->map(function ($spice) {
|
||||
return $spice->name . ' (' . round($spice->pivot->amount_in_percent) . '%)';
|
||||
});
|
||||
|
||||
if ($record->spices->count() > 3) {
|
||||
$spices->push('... weitere');
|
||||
}
|
||||
|
||||
return $spices->implode(', ');
|
||||
})
|
||||
->limit(50),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRecord($id) {
|
||||
return Recipe::with('spices')->findOrFail($id); // Eager load spices relationship
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListRecipes::route('/'),
|
||||
'create' => Pages\CreateRecipe::route('/create'),
|
||||
'edit' => Pages\EditRecipe::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
public static function getPluralLabel(): string
|
||||
{
|
||||
return 'Rezepte';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\RecipeResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\RecipeResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Models\RecipeSpice;
|
||||
|
||||
class CreateRecipe extends CreateRecord
|
||||
{
|
||||
protected static string $resource = RecipeResource::class;
|
||||
|
||||
protected function handleRecordCreation(array $data): Model
|
||||
{
|
||||
$record = static::getModel()::create($data);
|
||||
|
||||
foreach ($data['spices'] as $spice) {
|
||||
RecipeSpice::create([
|
||||
'recipe_id' => $record->id,
|
||||
'spice_id' => $spice['spice_id'],
|
||||
'amount_in_percent' => $spice['amount_in_percent'],
|
||||
]);
|
||||
}
|
||||
return $record;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace App\Filament\User\Resources\RecipeResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\RecipeResource;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditRecipe extends EditRecord
|
||||
{
|
||||
protected static string $resource = RecipeResource::class;
|
||||
|
||||
protected function afterSave(): void
|
||||
{
|
||||
$this->syncSpices();
|
||||
}
|
||||
|
||||
protected function syncSpices(): void
|
||||
{
|
||||
$recipe = $this->record;
|
||||
|
||||
$spices = collect($this->data['spices'] ?? [])
|
||||
->mapWithKeys(function ($spice) {
|
||||
return [
|
||||
$spice['spice_id'] => ['amount_in_percent' => $spice['amount_in_percent']],
|
||||
];
|
||||
})
|
||||
->toArray();
|
||||
|
||||
$recipe->spices()->sync($spices);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\RecipeResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\RecipeResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListRecipes extends ListRecords
|
||||
{
|
||||
protected static string $resource = RecipeResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources;
|
||||
|
||||
use App\Filament\User\Resources\SpiceResource\Pages;
|
||||
use App\Filament\User\Resources\SpiceResource\RelationManagers;
|
||||
use App\Models\Spice;
|
||||
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 SpiceResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Spice::class;
|
||||
protected static ?string $navgationLabel = 'Rohstoffe';
|
||||
protected static ?string $modelLabel = 'Rohstoff';
|
||||
protected static ?string $slug = 'rohstoffe';
|
||||
protected static ?string $navigationGroup = 'Lager';
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-cube';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('name')
|
||||
->required()
|
||||
->maxLength(255),
|
||||
Forms\Components\Select::make('unit')
|
||||
->label('Mengeneinheit')
|
||||
->native(false)
|
||||
->searchable()
|
||||
->options([
|
||||
'grams' => '(Kilo-)Gramm',
|
||||
'pieces' => 'Stück',
|
||||
'liter' => 'Liter',
|
||||
])
|
||||
->required(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('name')
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('quantity')->label('Menge lagernd')
|
||||
->formatStateUsing(function (Spice $record) {
|
||||
if ($record->unit == 'grams') {
|
||||
return ($record->quantity >= 1000) ? round($record->quantity / 1000, 2) . 'kg' : $record->quantity . 'g';
|
||||
}elseif($record->unit == 'pieces'){
|
||||
return $record->quantity . ' Stück';
|
||||
}elseif($record->unit == 'liter'){
|
||||
return $record->quantity . ' Liter';
|
||||
}else{
|
||||
return $record->quantity . ' ' . ucfirst($record->unit);
|
||||
}
|
||||
}),
|
||||
])
|
||||
->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\ListSpices::route('/'),
|
||||
'create' => Pages\CreateSpice::route('/create'),
|
||||
'edit' => Pages\EditSpice::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
public static function getPluralLabel(): string
|
||||
{
|
||||
return 'Rohstoffe'; // Setze hier deinen eigenen Pluralbegriff ein
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\SpiceResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\SpiceResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateSpice extends CreateRecord
|
||||
{
|
||||
protected static string $resource = SpiceResource::class;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\SpiceResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\SpiceResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditSpice extends EditRecord
|
||||
{
|
||||
protected static string $resource = SpiceResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\SpiceResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\SpiceResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListSpices extends ListRecords
|
||||
{
|
||||
protected static string $resource = SpiceResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\SpiceResource\Widgets;
|
||||
|
||||
use Leandrocfe\FilamentApexCharts\Widgets\ApexChartWidget;
|
||||
use Filament\Support\RawJs;
|
||||
use App\Models\Spice;
|
||||
|
||||
class SpiceChart extends ApexChartWidget {
|
||||
protected static ?string $chartId = 'spiceChart';
|
||||
protected static ?string $heading = 'Rohstoffmengen im Lager';
|
||||
|
||||
protected function getOptions(): array {
|
||||
$data = Spice::all();
|
||||
|
||||
$data_array = [];
|
||||
$labels = [];
|
||||
foreach ($data as $label) {
|
||||
if ($label->unit == 'grams') {
|
||||
$data_array[] = round($label->quantity / 1000, 2);
|
||||
$labels[] = $label->name . ' (kg)';
|
||||
} else {
|
||||
$data_array[] = $label->quantity;
|
||||
if ($label->unit == 'pieces') {
|
||||
$labels[] = $label->name . ' (Stück)';
|
||||
} elseif ($label->unit == 'liter') {
|
||||
$labels[] = $label->name . ' (Liter)';
|
||||
} else {
|
||||
$labels[] = $label->name . ' (' . ucfirst($label->unit) . ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return [
|
||||
'chart' => [
|
||||
'type' => 'bar',
|
||||
'height' => 300,
|
||||
],
|
||||
'series' => [
|
||||
[
|
||||
'name' => 'BasicBarChart',
|
||||
'data' => $data_array,
|
||||
],
|
||||
],
|
||||
'xaxis' => [
|
||||
'categories' => $labels,
|
||||
'labels' => [
|
||||
'style' => [
|
||||
'fontFamily' => 'inherit',
|
||||
],
|
||||
],
|
||||
],
|
||||
'yaxis' => [
|
||||
'labels' => [
|
||||
'style' => [
|
||||
'fontFamily' => 'inherit',
|
||||
],
|
||||
],
|
||||
],
|
||||
'colors' => ['#f59e0b'],
|
||||
'plotOptions' => [
|
||||
'bar' => [
|
||||
'borderRadius' => 3,
|
||||
'horizontal' => false,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function extraJsOptions(): ?RawJs {
|
||||
return RawJs::make(<<<'JS'
|
||||
{
|
||||
xaxis: {
|
||||
labels: {
|
||||
formatter: function (val, timestamp, opts) {
|
||||
return val.replace('( ', '(')
|
||||
}
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
formatter: function (val, opt) {
|
||||
return val+''+opt.w.globals.labels[opt.dataPointIndex].match(/\((.*?)\)/)[1]
|
||||
},
|
||||
dropShadow: {
|
||||
enabled: true
|
||||
},
|
||||
}
|
||||
}
|
||||
JS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources;
|
||||
|
||||
use App\Filament\User\Resources\SpiceSupplierResource\Pages;
|
||||
use App\Filament\User\Resources\SpiceSupplierResource\RelationManagers;
|
||||
use App\Models\SpiceSupplier;
|
||||
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;
|
||||
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
|
||||
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
|
||||
class SpiceSupplierResource extends Resource
|
||||
{
|
||||
protected static ?string $model = SpiceSupplier::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-arrow-down-on-square';
|
||||
|
||||
protected static ?string $navgationLabel = 'Rohstofflieferungen';
|
||||
protected static ?string $modelLabel = 'Rohstofflieferungen';
|
||||
protected static ?string $slug = 'rohstofflieferung';
|
||||
protected static ?string $navigationGroup = 'Waren Ein/Ausgang';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Select::make('spice_id')
|
||||
->label('Rohstoff')
|
||||
->relationship('spice', 'name')
|
||||
->required()
|
||||
->preload()
|
||||
->searchable(),
|
||||
Select::make('supplier_id')
|
||||
->label('Lieferant')
|
||||
->relationship('supplier', 'name')
|
||||
->required()
|
||||
->preload()
|
||||
->searchable(),
|
||||
|
||||
TextInput::make('amount')
|
||||
->label('Liefermenge')
|
||||
->numeric()
|
||||
->required()
|
||||
->rules(['min:0.01'])
|
||||
->disabled(fn($record) => !is_null($record)),
|
||||
|
||||
DatePicker::make('supplied_at')
|
||||
->label('Lieferdatum')
|
||||
->default(now())
|
||||
->required(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('spice.name')->label('Rohstoff')->searchable(),
|
||||
TextColumn::make('supplier.name')->label('Lieferant')->searchable(),
|
||||
TextColumn::make('amount')->label('Liefermenge'),
|
||||
TextColumn::make('supplied_at')->label('Lieferdatum')->date('d.m.Y'), // Lieferdatum in der Tabelle anzeigen
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected static function getTableActions(): array
|
||||
{
|
||||
return [
|
||||
DeleteAction::make()
|
||||
->before(function ($record, DeleteAction $action) {
|
||||
// Custom check logic
|
||||
|
||||
if (true) {
|
||||
// Notify user about the restriction
|
||||
Notification::make()
|
||||
->title('Deletion Error')
|
||||
->body("This record cannot be deleted because it has related records.")
|
||||
->status('danger')
|
||||
->send();
|
||||
|
||||
// Cancel the deletion
|
||||
$action->cancel();
|
||||
}
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListSpiceSuppliers::route('/'),
|
||||
'create' => Pages\CreateSpiceSupplier::route('/create'),
|
||||
'edit' => Pages\EditSpiceSupplier::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
public static function getPluralLabel(): string
|
||||
{
|
||||
return 'Rohstofflieferungen';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\SpiceSupplierResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\SpiceSupplierResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Models\Spice;
|
||||
|
||||
class CreateSpiceSupplier extends CreateRecord
|
||||
{
|
||||
protected static string $resource = SpiceSupplierResource::class;
|
||||
|
||||
protected function handleRecordCreation(array $data): Model {
|
||||
$record = static::getModel()::create($data);
|
||||
|
||||
Spice::findOrFail($data['spice_id'])->increment('quantity', $data['amount']);
|
||||
return $record;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\SpiceSupplierResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\SpiceSupplierResource;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
use Filament\Notifications\Notification;
|
||||
use App\Models\Spice;
|
||||
|
||||
class EditSpiceSupplier extends EditRecord
|
||||
{
|
||||
protected static string $resource = SpiceSupplierResource::class;
|
||||
|
||||
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
\Filament\Actions\DeleteAction::make()->before(function ($record) {
|
||||
if (!$record->spice || $record->spice->quantity < $record->amount) {
|
||||
Notification::make()
|
||||
->warning()
|
||||
->title('Fehler!')
|
||||
->body('Nicht mehr genügend ' . htmlentities($record->spice->name) . ' im Lager (bräuchte mindestens Liefermenge um Lieferung rückgängig zu machen)')
|
||||
->send();
|
||||
$this->halt();
|
||||
return false;
|
||||
}
|
||||
Spice::findOrFail($record->spice->id)->decrement('quantity', $record->amount);
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\SpiceSupplierResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\SpiceSupplierResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListSpiceSuppliers extends ListRecords
|
||||
{
|
||||
protected static string $resource = SpiceSupplierResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources;
|
||||
|
||||
use App\Filament\User\Resources\SupplierResource\Pages;
|
||||
use App\Filament\User\Resources\SupplierResource\RelationManagers;
|
||||
use App\Models\Supplier;
|
||||
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 SupplierResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Supplier::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-user';
|
||||
protected static ?string $navigationLabel = 'Lieferanten';
|
||||
protected static ?string $modelLabel = 'Lieferant';
|
||||
protected static ?string $slug = 'lieferant';
|
||||
protected static ?string $navigationGroup = 'CRM';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('name')
|
||||
->required()
|
||||
->maxLength(255),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('name')
|
||||
->searchable(),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
Tables\Columns\TextColumn::make('updated_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->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\ListSuppliers::route('/'),
|
||||
'create' => Pages\CreateSupplier::route('/create'),
|
||||
'edit' => Pages\EditSupplier::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
public static function getPluralLabel(): string
|
||||
{
|
||||
return 'Lieferanten';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\SupplierResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\SupplierResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateSupplier extends CreateRecord
|
||||
{
|
||||
protected static string $resource = SupplierResource::class;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\SupplierResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\SupplierResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditSupplier extends EditRecord
|
||||
{
|
||||
protected static string $resource = SupplierResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Resources\SupplierResource\Pages;
|
||||
|
||||
use App\Filament\User\Resources\SupplierResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListSuppliers extends ListRecords
|
||||
{
|
||||
protected static string $resource = SupplierResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user