Compare commits
No commits in common. "cbda830c5bc30f06566ac4f5fc4908f9c289fa69" and "e49b7515e004274815e92ee1f9d9aa6e532c7a96" have entirely different histories.
cbda830c5b
...
e49b7515e0
@ -1,11 +0,0 @@
|
||||
{
|
||||
"version": "0.0.1",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "t2t-dev",
|
||||
"runtimeExecutable": "php",
|
||||
"runtimeArgs": ["artisan", "serve", "--port=8123"],
|
||||
"port": 8123
|
||||
}
|
||||
]
|
||||
}
|
||||
10
AGENTS.md
10
AGENTS.md
@ -22,16 +22,6 @@ ### 3. Átállás és Modernizáció
|
||||
- **Auditálás**: Használd a `BaseAuditable` osztályt minden új modellnél a konzisztens követhetőség érdekében.
|
||||
- **Refaktorálás**: Ha egy legacy kontroller/nézet jelentős átalakítást igényel, javasold annak áthelyezését egy Filament Resource-ba.
|
||||
|
||||
# Feature flag rendszer (Laravel Pennant)
|
||||
|
||||
Az alkalmazásban deploy nélkül, admin felületről (`/admin/feature-flags`) ki/bekapcsolható funkciókhoz a `laravel/pennant` csomagra épülő, saját `feature_flags`/`feature_flag_overrides` táblákkal kiegészített rendszer van. Új feature flag bevezetésekor vagy a rendszer módosításakor:
|
||||
|
||||
- **NE** írj kézzel új `Feature::define()` hívást — a `FeatureFlagRegistrar::registerAll()` (hívva: `AppServiceProvider::boot()`) automatikusan regisztrálja az admin felületen létrehozott flageket.
|
||||
- **NE** használd a Pennant saját tárolóját (`Feature::for($user)->activate()`) tartós, admin felületről törölhetetlen felhasználói döntésekhez — a `Feature::purge()` egy adott flag nevére **minden** scope-ot (minden felhasználó feloldását) töröl. Az explicit egyéni felülbírálásokat ezért a saját `feature_flag_overrides` tábla (`FeatureFlagOverride` modell) tárolja, amit a resolver néz meg elsőként, mielőtt a szabály-alapú (enabled/stage/role) logikára térne.
|
||||
- `FeatureFlagOverride` (vagy bármi, ami Pennant-cache-invalidáló Observerrel rendelkezik) módosítása mindig **model-instance-on keresztül** történjen (`$model->save()`/`$model->delete()`), soha ne query builder `->update()`/`->delete()`-tel — csak az előbbi váltja ki az Eloquent modell-eseményeket, amikre az Observerek épülnek.
|
||||
- A `FeatureFlagAdmin` nevű flag saját magát a feature flag admin felületet védi (`FeatureFlagPolicy`/`FeatureFlagOverridePolicy` → `Feature::for($user)->active('FeatureFlagAdmin')`) — jelenleg kizárólag `developer` szerepkör éri el, minden stage-en.
|
||||
- Teljes technikai leírás (architektúra, talált hibák, tesztelési minták): `storage/app/private/docs/feature-flag-rendszer.md`.
|
||||
|
||||
# Projekt-specifikus szabályok
|
||||
|
||||
- **API válaszok**: Használd a `flugger/laravel-responder` csomagot (`responder()` helper) minden API válaszhoz. Kövesd az `OrderController.php`-ban látható mintát.
|
||||
|
||||
@ -1,336 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\ProfitCenter;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Monolog\Formatter\LineFormatter;
|
||||
|
||||
class CheckApiLog extends Command
|
||||
{
|
||||
protected $signature = 'app:check-api-log {file? : A storage/logs alatti API log fájl neve (pl. api-2026-07-21.log). Ha nincs megadva, a fájlnév szerint legutolsó api-*.log fájlt választja.}';
|
||||
|
||||
protected $description = 'A storage/logs/api-*.log fájlban szereplő /api/v1/order/unprocessed válaszok ellenőrzése: profit center hooreycaDataActive állapota és a HooreycaVolumen/HooreycaUnitPrice számított mezők helyessége.';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$filePath = $this->resolveLogFile($this->argument('file'));
|
||||
|
||||
if (! $filePath) {
|
||||
$this->error($this->argument('file')
|
||||
? 'A megadott log fájl nem található: storage/logs/'.$this->argument('file')
|
||||
: 'Nem található api-*.log fájl a storage/logs könyvtárban.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$this->info('Elemzett fájl: '.$filePath);
|
||||
|
||||
[$totalQueries, $totalOrders] = $this->preprocess($filePath);
|
||||
|
||||
if ($totalQueries === 0) {
|
||||
$this->warn('Nem található "unprocessed" API válasz (pagination mezővel) ebben a fájlban.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->info("Talált lekérdezések (API hívások): {$totalQueries}, összesen {$totalOrders} megrendeléssel.");
|
||||
|
||||
$errorLogPath = $this->errorLogPathFor($filePath);
|
||||
$errorLogger = Log::build([
|
||||
'driver' => 'single',
|
||||
'path' => $errorLogPath,
|
||||
]);
|
||||
|
||||
$formatter = new LineFormatter(null, null, true, true);
|
||||
$formatter->setJsonPrettyPrint(true);
|
||||
foreach ($errorLogger->getLogger()->getHandlers() as $handler) {
|
||||
$handler->setFormatter($formatter);
|
||||
}
|
||||
|
||||
$bar = $this->output->createProgressBar($totalOrders);
|
||||
$bar->start();
|
||||
|
||||
$queryIndex = 0;
|
||||
$queriesWithErrors = 0;
|
||||
$ordersWithErrors = 0;
|
||||
$itemErrorCount = 0;
|
||||
$profitCenterErrorCount = 0;
|
||||
|
||||
$handle = fopen($filePath, 'r');
|
||||
while (($line = fgets($handle)) !== false) {
|
||||
$responseBody = $this->extractUnprocessedResponse($line);
|
||||
if ($responseBody === null) {
|
||||
continue;
|
||||
}
|
||||
$queryIndex++;
|
||||
$queryHasError = false;
|
||||
|
||||
foreach ($responseBody['data'] ?? [] as $order) {
|
||||
$orderErrors = $this->checkProfitCenter($order);
|
||||
if ($orderErrors) {
|
||||
$profitCenterErrorCount++;
|
||||
}
|
||||
|
||||
foreach ($order['items'] ?? [] as $item) {
|
||||
$itemErrors = $this->checkItemCalculations($item);
|
||||
if ($itemErrors) {
|
||||
$itemErrorCount++;
|
||||
foreach ($itemErrors as $err) {
|
||||
$orderErrors[] = 'item id='.($item['id'] ?? '?').': '.$err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($orderErrors) {
|
||||
$ordersWithErrors++;
|
||||
$queryHasError = true;
|
||||
$errorLogger->error('Hibás megrendelés találat', [
|
||||
'sourceQueryIndex' => $queryIndex,
|
||||
'orderId' => $order['id'] ?? null,
|
||||
'errors' => $orderErrors,
|
||||
'order' => $order,
|
||||
]);
|
||||
}
|
||||
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
if ($queryHasError) {
|
||||
$queriesWithErrors++;
|
||||
}
|
||||
}
|
||||
fclose($handle);
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine(2);
|
||||
|
||||
$this->table(['Metrika', 'Érték'], [
|
||||
['Vizsgált lekérdezések (API hívások) száma', $queryIndex],
|
||||
['Hibát tartalmazó lekérdezések száma', $queriesWithErrors],
|
||||
['Vizsgált megrendelések száma', $totalOrders],
|
||||
['Hibás megrendelések száma', $ordersWithErrors],
|
||||
['Hibás tétel(item) számítás találatok száma', $itemErrorCount],
|
||||
['Profit center hiba találatok száma', $profitCenterErrorCount],
|
||||
]);
|
||||
|
||||
if ($ordersWithErrors > 0) {
|
||||
$this->warn('Hibás rendelések naplózva ide: '.$errorLogPath);
|
||||
} else {
|
||||
$this->info('Nem található hiba.');
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
private function resolveLogFile(?string $file): ?string
|
||||
{
|
||||
$logsDir = storage_path('logs');
|
||||
|
||||
if ($file) {
|
||||
$path = $logsDir.DIRECTORY_SEPARATOR.$file;
|
||||
|
||||
return is_file($path) ? $path : null;
|
||||
}
|
||||
|
||||
$dated = [];
|
||||
foreach (glob($logsDir.DIRECTORY_SEPARATOR.'api-*.log') ?: [] as $path) {
|
||||
if (preg_match('/api-(\d{4}-\d{2}-\d{2})\.log$/', basename($path), $m)) {
|
||||
$dated[$m[1]] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
if (! $dated) {
|
||||
return null;
|
||||
}
|
||||
|
||||
krsort($dated);
|
||||
|
||||
return reset($dated);
|
||||
}
|
||||
|
||||
private function errorLogPathFor(string $filePath): string
|
||||
{
|
||||
$suffix = preg_match('/api-(\d{4}-\d{2}-\d{2})\.log$/', basename($filePath), $m)
|
||||
? $m[1]
|
||||
: now()->format('Y-m-d');
|
||||
|
||||
return storage_path("logs/api-check-errors-{$suffix}.log");
|
||||
}
|
||||
|
||||
private function preprocess(string $filePath): array
|
||||
{
|
||||
$totalQueries = 0;
|
||||
$totalOrders = 0;
|
||||
|
||||
$handle = fopen($filePath, 'r');
|
||||
while (($line = fgets($handle)) !== false) {
|
||||
$responseBody = $this->extractUnprocessedResponse($line);
|
||||
if ($responseBody === null) {
|
||||
continue;
|
||||
}
|
||||
$totalQueries++;
|
||||
$totalOrders += count($responseBody['data'] ?? []);
|
||||
}
|
||||
fclose($handle);
|
||||
|
||||
return [$totalQueries, $totalOrders];
|
||||
}
|
||||
|
||||
/**
|
||||
* Csak a /api/v1/order/unprocessed válaszokat ismeri fel (a "pagination" kulcs csak ott van jelen,
|
||||
* a setProcessed/status válaszokban nincs).
|
||||
*/
|
||||
private function extractUnprocessedResponse(string $line): ?array
|
||||
{
|
||||
if (! str_contains($line, 'API response')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$context = $this->extractBalancedJson($line, 'API response');
|
||||
if ($context === null || ! isset($context['responseBody']) || ! is_array($context['responseBody'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$body = $context['responseBody'];
|
||||
if (! isset($body['pagination'], $body['data']) || ! is_array($body['data'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Monolog sorvégi %context%/%extra% JSON-jából kiszedi az első kapcsos zárójel-pártól
|
||||
* a hozzá tartozó záró zárójelig terjedő, kiegyensúlyozott JSON-t (idézőjelen belüli
|
||||
* kapcsos zárójeleket figyelmen kívül hagyva), hogy a sor végén álló extra tartalom
|
||||
* (pl. üres %extra% tömb) ne zavarja a dekódolást.
|
||||
*/
|
||||
private function extractBalancedJson(string $line, string $marker): ?array
|
||||
{
|
||||
$pos = strpos($line, $marker);
|
||||
if ($pos === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$jsonStart = strpos($line, '{', $pos);
|
||||
if ($jsonStart === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$depth = 0;
|
||||
$inString = false;
|
||||
$escape = false;
|
||||
$end = null;
|
||||
$len = strlen($line);
|
||||
|
||||
for ($i = $jsonStart; $i < $len; $i++) {
|
||||
$ch = $line[$i];
|
||||
if ($escape) {
|
||||
$escape = false;
|
||||
continue;
|
||||
}
|
||||
if ($ch === '\\') {
|
||||
$escape = true;
|
||||
continue;
|
||||
}
|
||||
if ($ch === '"') {
|
||||
$inString = ! $inString;
|
||||
continue;
|
||||
}
|
||||
if ($inString) {
|
||||
continue;
|
||||
}
|
||||
if ($ch === '{') {
|
||||
$depth++;
|
||||
} elseif ($ch === '}') {
|
||||
$depth--;
|
||||
if ($depth === 0) {
|
||||
$end = $i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($end === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decoded = json_decode(substr($line, $jsonStart, $end - $jsonStart + 1), true);
|
||||
|
||||
return json_last_error() === JSON_ERROR_NONE ? $decoded : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A profit center hooreycaDataActive állapotát a JELENLEGI DB-állapot alapján ellenőrzi
|
||||
* (a log maga nem tartalmazza a nyers flag értékét), és jelzi, ha a válasz tartalma
|
||||
* (profitCenterHooreycaId/profitCenterName jelenléte) nem egyezik a DB jelenlegi állapotával.
|
||||
*/
|
||||
private function checkProfitCenter(array $order): array
|
||||
{
|
||||
$errors = [];
|
||||
$profitCenterId = $order['profit_center_id'] ?? null;
|
||||
$profitCenter = $profitCenterId ? ProfitCenter::withTrashed()->find($profitCenterId) : null;
|
||||
$dbActive = $profitCenter?->hooreycaDataActive;
|
||||
$hasResponseData = ! empty($order['profitCenterHooreycaId']) || ! empty($order['profitCenterName']);
|
||||
|
||||
if (! $profitCenter) {
|
||||
$errors[] = "profit_center_id={$profitCenterId} nem található a DB-ben (törölve vagy soha nem létezett)";
|
||||
} elseif ($profitCenter->trashed()) {
|
||||
$errors[] = "profit_center_id={$profitCenterId} soft-deleted a DB-ben, mégis szerepelt a válaszban";
|
||||
} elseif (! $dbActive) {
|
||||
$errors[] = "profit_center_id={$profitCenterId} hooreycaDataActive JELENLEG nem aktív a DB-ben, mégis szerepelt a válaszban";
|
||||
}
|
||||
|
||||
if ($profitCenter && ! $profitCenter->trashed() && $dbActive && ! $hasResponseData) {
|
||||
$errors[] = "profit_center_id={$profitCenterId} a DB-ben aktív, de a válaszból hiányzik a profitCenterHooreycaId/profitCenterName";
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* A HooreycaVolumen/HooreycaUnitPrice mezőket a jelenleg érvényes (OrderController::
|
||||
* convertItemCollectionToHooreycaAPIItems-ben lévő) képlettel újraszámolja a naplózott
|
||||
* nyers mezőkből, és összeveti a naplózott számított értékkel.
|
||||
*/
|
||||
private function checkItemCalculations(array $item): array
|
||||
{
|
||||
$errors = [];
|
||||
$quantity = $item['quantity'] ?? null;
|
||||
$unitMultiplier = $item['unitMultiplier'] ?? null;
|
||||
$hooreycaMultiplier = $item['HooreycaMultiplier'] ?? null;
|
||||
$price = $item['price'] ?? null;
|
||||
$loggedVolumen = $item['HooreycaVolumen'] ?? null;
|
||||
$loggedUnitPrice = $item['HooreycaUnitPrice'] ?? null;
|
||||
|
||||
if ($hooreycaMultiplier === null) {
|
||||
$expectedVolumen = null;
|
||||
$expectedUnitPrice = null;
|
||||
} else {
|
||||
$expectedVolumen = $quantity * $hooreycaMultiplier;
|
||||
$expectedUnitPrice = $expectedVolumen != 0
|
||||
? ($quantity * $unitMultiplier * $price) / $expectedVolumen
|
||||
: null;
|
||||
}
|
||||
|
||||
if (! $this->numbersMatch($expectedVolumen, $loggedVolumen)) {
|
||||
$errors[] = sprintf('HooreycaVolumen eltér: várt=%s, naplózott=%s', json_encode($expectedVolumen), json_encode($loggedVolumen));
|
||||
}
|
||||
if (! $this->numbersMatch($expectedUnitPrice, $loggedUnitPrice)) {
|
||||
$errors[] = sprintf('HooreycaUnitPrice eltér: várt=%s, naplózott=%s', json_encode($expectedUnitPrice), json_encode($loggedUnitPrice));
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
private function numbersMatch($expected, $actual, float $epsilon = 0.0001): bool
|
||||
{
|
||||
if ($expected === null || $actual === null) {
|
||||
return $expected === $actual;
|
||||
}
|
||||
|
||||
return abs((float) $expected - (float) $actual) < $epsilon;
|
||||
}
|
||||
}
|
||||
@ -158,7 +158,7 @@ private function sendNotification(int $orderArchiveId)
|
||||
$to[] = $orderData['customNotificationEmail'];
|
||||
}
|
||||
|
||||
if (config('app.stage', 'DEV') == 'PROD') {
|
||||
if (env('APP_STAGE', 'DEV') == 'PROD') {
|
||||
$to[] = $orderData['supplier']['orderEmail'];
|
||||
if ($orderData['supplier']['orderEmail2']) {
|
||||
$to[] = $orderData['supplier']['orderEmail2'];
|
||||
|
||||
@ -1,172 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use App\Models\ProfitCenter;
|
||||
use App\Models\ProfitCenterSupplierCode;
|
||||
use App\Models\Supplier;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Filament\Forms\Contracts\HasForms;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Schemas\Components\Grid;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Components\Utilities\Get;
|
||||
use Filament\Schemas\Components\Utilities\Set;
|
||||
use Filament\Schemas\Schema;
|
||||
use Laravel\Pennant\Feature;
|
||||
|
||||
class SupplierCustomerCodes extends Page implements HasForms
|
||||
{
|
||||
use InteractsWithForms;
|
||||
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-identification';
|
||||
|
||||
protected string $view = 'filament.pages.supplier-customer-codes';
|
||||
|
||||
protected static ?string $title = 'Beszállítói vevőkódok';
|
||||
|
||||
protected static ?string $navigationLabel = 'Beszállítói vevőkódok';
|
||||
|
||||
protected static bool $shouldRegisterNavigation = false;
|
||||
|
||||
public ?array $data = [];
|
||||
|
||||
/**
|
||||
* Rollout-védelem: a SupplierCustomerCode Pennant flag alapján dől el, ki éri el ezt
|
||||
* az oldalt - a topbar menüpont is ezt hívja (ld. speed-button-nav-bar.blade.php).
|
||||
*/
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
if (! $user = auth()->user()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Feature::for($user)->active('SupplierCustomerCode');
|
||||
}
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
abort_unless(static::canAccess(), 403);
|
||||
|
||||
$this->form->fill();
|
||||
}
|
||||
|
||||
public function form(Schema $form): Schema
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Grid::make(2)
|
||||
->schema([
|
||||
Select::make('supplier_id')
|
||||
->label('Beszállító')
|
||||
->options(Supplier::query()->orderBy('name')->pluck('name', 'id'))
|
||||
->required()
|
||||
->searchable()
|
||||
->live()
|
||||
->afterStateUpdated(fn (?string $state, Set $set) => $this->loadSupplierData($state, $set)),
|
||||
TextInput::make('search')
|
||||
->label('Profitcenter keresése')
|
||||
->placeholder('Kezdjen el gépelni a szűküléshez...')
|
||||
->live(debounce: 300)
|
||||
->visible(fn (Get $get) => filled($get('supplier_id'))),
|
||||
]),
|
||||
Toggle::make('hasCustomerCode')
|
||||
->label('Vevőkód funkció aktív ennél a beszállítónál')
|
||||
->visible(fn (Get $get) => filled($get('supplier_id'))),
|
||||
Section::make('Profitcenterenkénti vevőkód')
|
||||
->visible(fn (Get $get) => filled($get('supplier_id')))
|
||||
->columns(3)
|
||||
->schema(fn (Get $get) => $this->profitCenterCodeFields($get('search'))),
|
||||
])
|
||||
->statePath('data');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, TextInput>
|
||||
*/
|
||||
private function profitCenterCodeFields(?string $search = null): array
|
||||
{
|
||||
return ProfitCenter::query()
|
||||
->when(filled($search), fn ($query) => $query->where('name', 'like', '%'.$search.'%'))
|
||||
->orderBy('name')
|
||||
->get()
|
||||
->map(fn (ProfitCenter $profitCenter) => TextInput::make("codes.{$profitCenter->id}")
|
||||
->label($profitCenter->name)
|
||||
->inlineLabel()
|
||||
->maxLength(255))
|
||||
->all();
|
||||
}
|
||||
|
||||
private function loadSupplierData(?string $supplierId, Set $set): void
|
||||
{
|
||||
$codes = [];
|
||||
|
||||
if ($supplierId) {
|
||||
$supplier = Supplier::find($supplierId);
|
||||
$set('hasCustomerCode', (bool) $supplier?->hasCustomerCode);
|
||||
|
||||
$codes = ProfitCenterSupplierCode::query()
|
||||
->where('supplier_id', $supplierId)
|
||||
->pluck('customerCode', 'profit_center_id')
|
||||
->all();
|
||||
} else {
|
||||
$set('hasCustomerCode', false);
|
||||
}
|
||||
|
||||
foreach (ProfitCenter::query()->pluck('id') as $profitCenterId) {
|
||||
$set("codes.{$profitCenterId}", $codes[$profitCenterId] ?? null);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getFormActions(): array
|
||||
{
|
||||
return [
|
||||
Action::make('save')
|
||||
->label('Mentés')
|
||||
->submit('save')
|
||||
->color('primary'),
|
||||
];
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$state = $this->form->getState();
|
||||
$supplierId = $state['supplier_id'];
|
||||
|
||||
Supplier::whereKey($supplierId)->update([
|
||||
'hasCustomerCode' => (bool) ($state['hasCustomerCode'] ?? false),
|
||||
]);
|
||||
|
||||
// A form aktuális szűrés (keresés) miatt csak a látható mezőket adná vissza a getState() -
|
||||
// a $this->data-ban viszont minden profitcenter kódja benne marad, mert a loadSupplierData()
|
||||
// mindegyiket explicit beállítja, függetlenül attól, hogy éppen renderelve van-e.
|
||||
foreach ($this->data['codes'] ?? [] as $profitCenterId => $customerCode) {
|
||||
$customerCode = is_string($customerCode) ? trim($customerCode) : $customerCode;
|
||||
|
||||
if (blank($customerCode)) {
|
||||
ProfitCenterSupplierCode::query()
|
||||
->where('supplier_id', $supplierId)
|
||||
->where('profit_center_id', $profitCenterId)
|
||||
->delete();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ProfitCenterSupplierCode::updateOrCreate(
|
||||
['supplier_id' => $supplierId, 'profit_center_id' => $profitCenterId],
|
||||
['customerCode' => $customerCode],
|
||||
);
|
||||
}
|
||||
|
||||
Notification::make()
|
||||
->title('Mentve')
|
||||
->body('A beszállító vevőkód adatai elmentve.')
|
||||
->success()
|
||||
->send();
|
||||
}
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\FeatureFlagOverrides;
|
||||
|
||||
use App\Filament\Resources\FeatureFlagOverrides\Pages\CreateFeatureFlagOverride;
|
||||
use App\Filament\Resources\FeatureFlagOverrides\Pages\EditFeatureFlagOverride;
|
||||
use App\Filament\Resources\FeatureFlagOverrides\Pages\ListFeatureFlagOverrides;
|
||||
use App\Filament\Resources\FeatureFlagOverrides\Schemas\FeatureFlagOverrideForm;
|
||||
use App\Filament\Resources\FeatureFlagOverrides\Tables\FeatureFlagOverridesTable;
|
||||
use App\Models\FeatureFlagOverride;
|
||||
use BackedEnum;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class FeatureFlagOverrideResource extends Resource
|
||||
{
|
||||
protected static ?string $model = FeatureFlagOverride::class;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-user-circle';
|
||||
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'Rendszer';
|
||||
|
||||
protected static ?string $navigationLabel = 'Egyéni felülbírálások';
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return FeatureFlagOverrideForm::configure($schema);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return FeatureFlagOverridesTable::configure($table);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListFeatureFlagOverrides::route('/'),
|
||||
'create' => CreateFeatureFlagOverride::route('/create'),
|
||||
'edit' => EditFeatureFlagOverride::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\FeatureFlagOverrides\Pages;
|
||||
|
||||
use App\Filament\Resources\FeatureFlagOverrides\FeatureFlagOverrideResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateFeatureFlagOverride extends CreateRecord
|
||||
{
|
||||
protected static string $resource = FeatureFlagOverrideResource::class;
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\FeatureFlagOverrides\Pages;
|
||||
|
||||
use App\Filament\Resources\FeatureFlagOverrides\FeatureFlagOverrideResource;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditFeatureFlagOverride extends EditRecord
|
||||
{
|
||||
protected static string $resource = FeatureFlagOverrideResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\FeatureFlagOverrides\Pages;
|
||||
|
||||
use App\Filament\Resources\FeatureFlagOverrides\FeatureFlagOverrideResource;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListFeatureFlagOverrides extends ListRecords
|
||||
{
|
||||
protected static string $resource = FeatureFlagOverrideResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\FeatureFlagOverrides\Schemas;
|
||||
|
||||
use App\Models\FeatureFlag;
|
||||
use App\Models\User;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class FeatureFlagOverrideForm
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Select::make('feature_flag_id')
|
||||
->label('Feature flag')
|
||||
->searchable()
|
||||
->options(fn () => FeatureFlag::pluck('label', 'id'))
|
||||
->disabledOn('edit')
|
||||
->required(),
|
||||
Select::make('user_id')
|
||||
->label('Felhasználó')
|
||||
->searchable()
|
||||
->options(fn () => User::pluck('name', 'id'))
|
||||
->disabledOn('edit')
|
||||
->required(),
|
||||
Toggle::make('active')
|
||||
->label('Bekapcsolva')
|
||||
->required(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\FeatureFlagOverrides\Tables;
|
||||
|
||||
use App\Models\FeatureFlag;
|
||||
use App\Models\User;
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class FeatureFlagOverridesTable
|
||||
{
|
||||
public static function configure(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('featureFlag.label')
|
||||
->label('Feature flag')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('user.name')
|
||||
->label('Felhasználó')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
IconColumn::make('active')
|
||||
->label('Állapot')
|
||||
->boolean(),
|
||||
TextColumn::make('updated_at')
|
||||
->label('Módosítva')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
])
|
||||
->filters([
|
||||
SelectFilter::make('feature_flag_id')
|
||||
->label('Feature flag')
|
||||
->options(fn () => FeatureFlag::pluck('label', 'id')),
|
||||
SelectFilter::make('user_id')
|
||||
->label('Felhasználó')
|
||||
->options(fn () => User::pluck('name', 'id'))
|
||||
->searchable(),
|
||||
])
|
||||
->recordActions([
|
||||
EditAction::make(),
|
||||
])
|
||||
->toolbarActions([
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\FeatureFlags;
|
||||
|
||||
use App\Filament\Resources\FeatureFlags\Pages\CreateFeatureFlag;
|
||||
use App\Filament\Resources\FeatureFlags\Pages\EditFeatureFlag;
|
||||
use App\Filament\Resources\FeatureFlags\Pages\ListFeatureFlags;
|
||||
use App\Filament\Resources\FeatureFlags\RelationManagers\OverridesRelationManager;
|
||||
use App\Filament\Resources\FeatureFlags\Schemas\FeatureFlagForm;
|
||||
use App\Filament\Resources\FeatureFlags\Tables\FeatureFlagsTable;
|
||||
use App\Models\FeatureFlag;
|
||||
use BackedEnum;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class FeatureFlagResource extends Resource
|
||||
{
|
||||
protected static ?string $model = FeatureFlag::class;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-flag';
|
||||
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'Rendszer';
|
||||
|
||||
protected static ?string $navigationLabel = 'Feature flagek';
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return FeatureFlagForm::configure($schema);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return FeatureFlagsTable::configure($table);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
OverridesRelationManager::class,
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListFeatureFlags::route('/'),
|
||||
'create' => CreateFeatureFlag::route('/create'),
|
||||
'edit' => EditFeatureFlag::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\FeatureFlags\Pages;
|
||||
|
||||
use App\Filament\Resources\FeatureFlags\FeatureFlagResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateFeatureFlag extends CreateRecord
|
||||
{
|
||||
protected static string $resource = FeatureFlagResource::class;
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\FeatureFlags\Pages;
|
||||
|
||||
use App\Filament\Resources\FeatureFlags\FeatureFlagResource;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditFeatureFlag extends EditRecord
|
||||
{
|
||||
protected static string $resource = FeatureFlagResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\FeatureFlags\Pages;
|
||||
|
||||
use App\Filament\Resources\FeatureFlags\FeatureFlagResource;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListFeatureFlags extends ListRecords
|
||||
{
|
||||
protected static string $resource = FeatureFlagResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\FeatureFlags\RelationManagers;
|
||||
|
||||
use App\Models\User;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class OverridesRelationManager extends RelationManager
|
||||
{
|
||||
protected static string $relationship = 'overrides';
|
||||
|
||||
protected static ?string $title = 'Egyéni felülbírálások';
|
||||
|
||||
public function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Select::make('user_id')
|
||||
->label('Felhasználó')
|
||||
->searchable()
|
||||
->options(fn () => User::pluck('name', 'id'))
|
||||
->disabledOn('edit')
|
||||
->required(),
|
||||
Toggle::make('active')
|
||||
->label('Bekapcsolva')
|
||||
->required(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->recordTitleAttribute('user.name')
|
||||
->columns([
|
||||
TextColumn::make('user.name')
|
||||
->label('Felhasználó')
|
||||
->searchable(),
|
||||
IconColumn::make('active')
|
||||
->label('Állapot')
|
||||
->boolean(),
|
||||
TextColumn::make('updated_at')
|
||||
->label('Módosítva')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
])
|
||||
->headerActions([
|
||||
CreateAction::make(),
|
||||
])
|
||||
->recordActions([
|
||||
EditAction::make(),
|
||||
DeleteAction::make(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\FeatureFlags\Schemas;
|
||||
|
||||
use App\Models\Role;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TagsInput;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class FeatureFlagForm
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Section::make('Alapadatok')
|
||||
->columnSpanFull()
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
->label('Kulcs')
|
||||
->helperText('Ezt a kulcsot kell használni a kódban: Feature::active(\'kulcs\')')
|
||||
->required()
|
||||
->unique(ignoreRecord: true)
|
||||
->maxLength(255),
|
||||
TextInput::make('label')
|
||||
->label('Megnevezés')
|
||||
->required()
|
||||
->maxLength(255),
|
||||
Textarea::make('description')
|
||||
->label('Leírás')
|
||||
->columnSpanFull(),
|
||||
Toggle::make('enabled')
|
||||
->label('Bekapcsolva (rendszer szintű kapcsoló)')
|
||||
->default(true),
|
||||
]),
|
||||
Section::make('Célzás')
|
||||
->columnSpanFull()
|
||||
->schema([
|
||||
TagsInput::make('stages')
|
||||
->label('Stage-ek')
|
||||
->helperText('Üresen hagyva minden környezeten aktív. Ismert értékek: DEV, TEST, PROD, d2dtst, e2etst.')
|
||||
->placeholder('pl. TEST'),
|
||||
Select::make('roles')
|
||||
->label('Szerepkörök')
|
||||
->helperText('Üresen hagyva minden szerepkörnek aktív.')
|
||||
->multiple()
|
||||
->options(fn () => Role::pluck('display_name', 'name')->filter()->all()),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -1,92 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\FeatureFlags\Tables;
|
||||
|
||||
use App\Models\FeatureFlag;
|
||||
use App\Models\FeatureFlagOverride;
|
||||
use App\Models\User;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Forms\Components\Radio;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Columns\ToggleColumn;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class FeatureFlagsTable
|
||||
{
|
||||
public static function configure(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('name')
|
||||
->label('Kulcs')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('label')
|
||||
->label('Megnevezés')
|
||||
->searchable(),
|
||||
ToggleColumn::make('enabled')
|
||||
->label('Bekapcsolva'),
|
||||
TextColumn::make('stages')
|
||||
->label('Stage-ek')
|
||||
->badge()
|
||||
->placeholder('mind'),
|
||||
TextColumn::make('roles')
|
||||
->label('Szerepkörök')
|
||||
->badge()
|
||||
->placeholder('mind'),
|
||||
TextColumn::make('updated_at')
|
||||
->label('Módosítva')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
])
|
||||
->recordActions([
|
||||
EditAction::make(),
|
||||
Action::make('userOverride')
|
||||
->label('Egyéni felülbírálás')
|
||||
->icon('heroicon-o-user')
|
||||
->form([
|
||||
Select::make('user_id')
|
||||
->label('Felhasználó')
|
||||
->searchable()
|
||||
->options(fn () => User::pluck('name', 'id'))
|
||||
->required(),
|
||||
Radio::make('override')
|
||||
->label('Állapot')
|
||||
->options([
|
||||
'inherit' => 'Örökölt (alapértelmezett szabály érvényes)',
|
||||
'on' => 'Bekapcsolva',
|
||||
'off' => 'Kikapcsolva',
|
||||
])
|
||||
->default('inherit')
|
||||
->required(),
|
||||
])
|
||||
->action(function (array $data, FeatureFlag $record) {
|
||||
$user = User::findOrFail($data['user_id']);
|
||||
|
||||
if ($data['override'] === 'inherit') {
|
||||
// model-instance delete() kell, hogy a FeatureFlagOverrideObserver
|
||||
// (Pennant purge) ténylegesen kiváltódjon - a query builder delete()
|
||||
// nem hívja meg a modell eseményeit
|
||||
FeatureFlagOverride::where('feature_flag_id', $record->id)
|
||||
->where('user_id', $user->id)
|
||||
->first()
|
||||
?->delete();
|
||||
} else {
|
||||
FeatureFlagOverride::updateOrCreate(
|
||||
['feature_flag_id' => $record->id, 'user_id' => $user->id],
|
||||
['active' => $data['override'] === 'on'],
|
||||
);
|
||||
}
|
||||
}),
|
||||
])
|
||||
->toolbarActions([
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -90,13 +90,13 @@ public function index(): View|Response|JsonResponse
|
||||
$m->subject('Árlista változás ');
|
||||
$m->to($emailAddress);
|
||||
/*
|
||||
if(config('app.stage')=='PROD'){
|
||||
if(env('APP_STAGE')=='PROD'){
|
||||
$m->to($emailAddress);
|
||||
}else{
|
||||
if(config('app.stage')=='TEST'){
|
||||
if(env('APP_STAGE')=='TEST'){
|
||||
$m->to('beszerzes@delirest.hu');
|
||||
}
|
||||
if(config('app.stage')=='DEV'){
|
||||
if(env('APP_STAGE')=='DEV'){
|
||||
$m->to('city@e98.hu');
|
||||
}
|
||||
|
||||
@ -303,13 +303,13 @@ private function notificationNewPriceList($priceListId)
|
||||
$destEmail->each(function ($emailAddress) use ($emailData) {
|
||||
\Mail::send('email.priceListChangeNotify', $emailData, function ($m) use ($emailAddress) {
|
||||
$m->subject('Árlista változás ');
|
||||
if (config('app.stage') == 'PROD') {
|
||||
if (env('APP_STAGE') == 'PROD') {
|
||||
$m->to($emailAddress);
|
||||
} else {
|
||||
if (config('app.stage') == 'TEST') {
|
||||
if (env('APP_STAGE') == 'TEST') {
|
||||
$m->to('beszerzes@delirest.hu');
|
||||
}
|
||||
if (config('app.stage') == 'DEV') {
|
||||
if (env('APP_STAGE') == 'DEV') {
|
||||
$m->to('city@e98.hu');
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,12 +102,6 @@ private function getSupplierDataFromRequest(Request $request)
|
||||
$data[$key] = $request->get($key);
|
||||
}
|
||||
$data['status'] = DbStatusFieldEnum::active;
|
||||
|
||||
// A hasCustomerCode mező csak akkor jelenik meg a formon, ha a SupplierCustomerCode
|
||||
// feature flag aktív - ilyenkor nem szabad null-t írni a NOT NULL boolean oszlopba.
|
||||
if ($request->has('hasCustomerCode')) {
|
||||
$data['hasCustomerCode'] = $request->boolean('hasCustomerCode');
|
||||
}
|
||||
if ($contactId = $request->get('contactId')) {
|
||||
$data['contactId'] = $contactId;
|
||||
} else {
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class FeatureFlag extends BaseAuditable
|
||||
{
|
||||
protected $casts = [
|
||||
'enabled' => 'boolean',
|
||||
'stages' => 'array',
|
||||
'roles' => 'array',
|
||||
];
|
||||
|
||||
public function overrides(): HasMany
|
||||
{
|
||||
return $this->hasMany(FeatureFlagOverride::class);
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class FeatureFlagOverride extends BaseAuditable
|
||||
{
|
||||
protected $casts = [
|
||||
'active' => 'boolean',
|
||||
];
|
||||
|
||||
public function featureFlag(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(FeatureFlag::class);
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,6 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
@ -34,11 +33,6 @@ public function suppliers(): BelongsToMany
|
||||
return $this->belongsToMany(Supplier::class)->withTimestamps();
|
||||
}
|
||||
|
||||
public function supplierCustomerCodes(): HasMany
|
||||
{
|
||||
return $this->hasMany(ProfitCenterSupplierCode::class);
|
||||
}
|
||||
|
||||
public function contact(): MorphMany
|
||||
{
|
||||
return $this->morphMany(Contact::class, 'contactable');
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class ProfitCenterSupplierCode extends BaseAuditable
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $guarded = ['id', 'created_at', 'updated_at'];
|
||||
|
||||
public function profitCenter(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ProfitCenter::class);
|
||||
}
|
||||
|
||||
public function supplier(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Supplier::class);
|
||||
}
|
||||
}
|
||||
@ -44,11 +44,6 @@ public function profitCenter(): BelongsToMany
|
||||
return $this->belongsToMany(ProfitCenter::class)->withTimestamps();
|
||||
}
|
||||
|
||||
public function customerCodes(): HasMany
|
||||
{
|
||||
return $this->hasMany(ProfitCenterSupplierCode::class);
|
||||
}
|
||||
|
||||
public function users(): HasMany
|
||||
{
|
||||
return $this->hasMany(User::class);
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\FeatureFlag;
|
||||
use Laravel\Pennant\Feature;
|
||||
|
||||
class FeatureFlagObserver
|
||||
{
|
||||
/**
|
||||
* A Pennant lazy módon, scope-onként gyorsítótárazza a feloldott értékeket -
|
||||
* mentés/törlés után purge-elni kell, hogy a módosítás azonnal érvényesüljön.
|
||||
*/
|
||||
public function saved(FeatureFlag $flag): void
|
||||
{
|
||||
Feature::purge($flag->name);
|
||||
}
|
||||
|
||||
public function deleted(FeatureFlag $flag): void
|
||||
{
|
||||
Feature::purge($flag->name);
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\FeatureFlagOverride;
|
||||
use Laravel\Pennant\Feature;
|
||||
|
||||
class FeatureFlagOverrideObserver
|
||||
{
|
||||
/**
|
||||
* Létrehozás/módosítás/törlés után csak az érintett usernek a gyorsítótárazott
|
||||
* Pennant-feloldását érvénytelenítjük, a többi felhasználót nem érinti.
|
||||
*/
|
||||
public function saved(FeatureFlagOverride $override): void
|
||||
{
|
||||
$this->forget($override);
|
||||
}
|
||||
|
||||
public function deleted(FeatureFlagOverride $override): void
|
||||
{
|
||||
$this->forget($override);
|
||||
}
|
||||
|
||||
private function forget(FeatureFlagOverride $override): void
|
||||
{
|
||||
$user = $override->user;
|
||||
$flag = $override->featureFlag;
|
||||
|
||||
if ($user && $flag) {
|
||||
Feature::for($user)->forget($flag->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\FeatureFlagOverride;
|
||||
use App\Models\User;
|
||||
use Laravel\Pennant\Feature;
|
||||
|
||||
class FeatureFlagOverridePolicy
|
||||
{
|
||||
/**
|
||||
* Ugyanaz a "FeatureFlagAdmin" flag szabályozza a hozzáférést, mint a
|
||||
* FeatureFlagPolicy-nál - a felülbírálások ugyanannak a jogosultsági
|
||||
* körnek a részei, mint maguk a flagek.
|
||||
*/
|
||||
protected function hasAccess(User $user): bool
|
||||
{
|
||||
return Feature::for($user)->active('FeatureFlagAdmin');
|
||||
}
|
||||
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $this->hasAccess($user);
|
||||
}
|
||||
|
||||
public function view(User $user, FeatureFlagOverride $featureFlagOverride): bool
|
||||
{
|
||||
return $this->hasAccess($user);
|
||||
}
|
||||
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $this->hasAccess($user);
|
||||
}
|
||||
|
||||
public function update(User $user, FeatureFlagOverride $featureFlagOverride): bool
|
||||
{
|
||||
return $this->hasAccess($user);
|
||||
}
|
||||
|
||||
public function delete(User $user, FeatureFlagOverride $featureFlagOverride): bool
|
||||
{
|
||||
return $this->hasAccess($user);
|
||||
}
|
||||
|
||||
public function deleteAny(User $user): bool
|
||||
{
|
||||
return $this->hasAccess($user);
|
||||
}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\FeatureFlag;
|
||||
use App\Models\User;
|
||||
use Laravel\Pennant\Feature;
|
||||
|
||||
class FeatureFlagPolicy
|
||||
{
|
||||
/**
|
||||
* A feature flag admin felület elérését magára a "FeatureFlagAdmin" flagre bízzuk,
|
||||
* hogy a hozzáférés szabályozása (stage/szerepkör/felhasználó szinten) deploy
|
||||
* nélkül, az admin felületről állítható legyen.
|
||||
*/
|
||||
protected function hasAccess(User $user): bool
|
||||
{
|
||||
return Feature::for($user)->active('FeatureFlagAdmin');
|
||||
}
|
||||
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $this->hasAccess($user);
|
||||
}
|
||||
|
||||
public function view(User $user, FeatureFlag $featureFlag): bool
|
||||
{
|
||||
return $this->hasAccess($user);
|
||||
}
|
||||
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $this->hasAccess($user);
|
||||
}
|
||||
|
||||
public function update(User $user, FeatureFlag $featureFlag): bool
|
||||
{
|
||||
return $this->hasAccess($user);
|
||||
}
|
||||
|
||||
public function delete(User $user, FeatureFlag $featureFlag): bool
|
||||
{
|
||||
return $this->hasAccess($user);
|
||||
}
|
||||
|
||||
public function deleteAny(User $user): bool
|
||||
{
|
||||
return $this->hasAccess($user);
|
||||
}
|
||||
}
|
||||
@ -2,10 +2,6 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\FeatureFlag;
|
||||
use App\Models\FeatureFlagOverride;
|
||||
use App\Observers\FeatureFlagObserver;
|
||||
use App\Observers\FeatureFlagOverrideObserver;
|
||||
use App\Repositories\AddressRepository;
|
||||
use App\Repositories\AddressRepositoryInterface;
|
||||
use App\Repositories\ContactRepository;
|
||||
@ -24,8 +20,6 @@
|
||||
use App\Repositories\ProfitCenterRepositoryInterface;
|
||||
use App\Repositories\SupplierRepository;
|
||||
use App\Repositories\SupplierRepositoryInterface;
|
||||
use App\Services\FeatureFlagRegistrar;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
@ -59,15 +53,6 @@ public function boot(): void
|
||||
{
|
||||
URL::forceScheme('https');
|
||||
|
||||
FeatureFlag::observe(FeatureFlagObserver::class);
|
||||
FeatureFlagOverride::observe(FeatureFlagOverrideObserver::class);
|
||||
|
||||
// Schema::hasTable védi a friss telepítést / migrate-et megelőző state-et,
|
||||
// amikor a feature_flags tábla még nem létezik.
|
||||
if (Schema::hasTable('feature_flags')) {
|
||||
app(FeatureFlagRegistrar::class)->registerAll();
|
||||
}
|
||||
|
||||
\Illuminate\Database\Schema\Blueprint::macro('userIdFields', function () {
|
||||
$this->unsignedInteger('created_by')->nullable();
|
||||
$this->unsignedInteger('updated_by')->nullable();
|
||||
@ -77,7 +62,6 @@ public function boot(): void
|
||||
\Livewire\Livewire::component('app.filament.pages.price-list-processor', \App\Filament\Pages\PriceListProcessor::class);
|
||||
\Livewire\Livewire::component('app.filament.auth.login', \App\Filament\Auth\Login::class);
|
||||
\Livewire\Livewire::component('app.filament.pages.calendar-test', \App\Filament\Pages\CalendarTest::class);
|
||||
\Livewire\Livewire::component('app.filament.pages.supplier-customer-codes', \App\Filament\Pages\SupplierCustomerCodes::class);
|
||||
\Livewire\Livewire::component('app.filament.widgets.delivery-calendar-widget', \App\Filament\Widgets\DeliveryCalendarWidget::class);
|
||||
|
||||
\Livewire\Livewire::component('app.filament.supplier-portal.resources.product-stocks.pages.list-product-stocks', \App\Filament\SupplierPortal\Resources\ProductStocks\Pages\ListProductStocks::class);
|
||||
@ -104,15 +88,6 @@ public function boot(): void
|
||||
\Livewire\Livewire::component('app.filament.resources.profit-center-supplier-schedules.pages.create-profit-center-supplier-schedule', \App\Filament\Resources\ProfitCenterSupplierSchedules\Pages\CreateProfitCenterSupplierSchedule::class);
|
||||
\Livewire\Livewire::component('app.filament.resources.profit-center-supplier-schedules.pages.edit-profit-center-supplier-schedule', \App\Filament\Resources\ProfitCenterSupplierSchedules\Pages\EditProfitCenterSupplierSchedule::class);
|
||||
|
||||
\Livewire\Livewire::component('app.filament.resources.feature-flags.pages.list-feature-flags', \App\Filament\Resources\FeatureFlags\Pages\ListFeatureFlags::class);
|
||||
\Livewire\Livewire::component('app.filament.resources.feature-flags.pages.create-feature-flag', \App\Filament\Resources\FeatureFlags\Pages\CreateFeatureFlag::class);
|
||||
\Livewire\Livewire::component('app.filament.resources.feature-flags.pages.edit-feature-flag', \App\Filament\Resources\FeatureFlags\Pages\EditFeatureFlag::class);
|
||||
\Livewire\Livewire::component('app.filament.resources.feature-flags.relation-managers.overrides-relation-manager', \App\Filament\Resources\FeatureFlags\RelationManagers\OverridesRelationManager::class);
|
||||
|
||||
\Livewire\Livewire::component('app.filament.resources.feature-flag-overrides.pages.list-feature-flag-overrides', \App\Filament\Resources\FeatureFlagOverrides\Pages\ListFeatureFlagOverrides::class);
|
||||
\Livewire\Livewire::component('app.filament.resources.feature-flag-overrides.pages.create-feature-flag-override', \App\Filament\Resources\FeatureFlagOverrides\Pages\CreateFeatureFlagOverride::class);
|
||||
\Livewire\Livewire::component('app.filament.resources.feature-flag-overrides.pages.edit-feature-flag-override', \App\Filament\Resources\FeatureFlagOverrides\Pages\EditFeatureFlagOverride::class);
|
||||
|
||||
\Livewire\Livewire::component('filament.livewire.notifications', \Filament\Livewire\Notifications::class);
|
||||
\Livewire\Livewire::component('filament.livewire.database-notifications', \Filament\Livewire\DatabaseNotifications::class);
|
||||
\Livewire\Livewire::component('filament.livewire.sidebar', \Filament\Livewire\Sidebar::class);
|
||||
|
||||
@ -13,8 +13,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||
* @var array<class-string, class-string>
|
||||
*/
|
||||
protected $policies = [
|
||||
\App\Models\FeatureFlag::class => \App\Policies\FeatureFlagPolicy::class,
|
||||
\App\Models\FeatureFlagOverride::class => \App\Policies\FeatureFlagOverridePolicy::class,
|
||||
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\FeatureFlag;
|
||||
use App\Models\FeatureFlagOverride;
|
||||
use App\Models\User;
|
||||
use Laravel\Pennant\Feature;
|
||||
|
||||
class FeatureFlagRegistrar
|
||||
{
|
||||
/**
|
||||
* Regisztrálja az összes adatbázisban tárolt feature flaget a Pennant-ban.
|
||||
*
|
||||
* Az egyéni felhasználói felülbírálásokat szándékosan NEM a Pennant saját
|
||||
* tárolóján (Feature::for($user)->activate()) keresztül kezeljük, mert a
|
||||
* Pennant purge() minden scope-ra töröl egy adott flag névre - egy admin
|
||||
* mentés (pl. enabled kikapcsolása) így törölné az explicit felülbírálásokat
|
||||
* is. Ezért az override-okat saját táblában tároljuk, a resolver pedig ezt
|
||||
* nézi meg elsőként; a Pennant tárolója így csak a szabály-alapú
|
||||
* alapértelmezett feloldást cache-eli, amit bármikor biztonságos purge-elni.
|
||||
*/
|
||||
public function registerAll(): void
|
||||
{
|
||||
FeatureFlag::all()->each(function (FeatureFlag $flag) {
|
||||
Feature::define($flag->name, function (?User $user) use ($flag) {
|
||||
if ($user) {
|
||||
$override = FeatureFlagOverride::where('feature_flag_id', $flag->id)
|
||||
->where('user_id', $user->id)
|
||||
->first();
|
||||
|
||||
if ($override) {
|
||||
return $override->active;
|
||||
}
|
||||
}
|
||||
|
||||
if (! $flag->enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($flag->stages && ! in_array(config('app.stage'), $flag->stages, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($flag->roles && (! $user || ! $user->hasRole($flag->roles))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -106,7 +106,6 @@ public function getExcelFieldPointer(): array
|
||||
19 => 'Vevői megnevezés',
|
||||
20 => 'Megjegyzés',
|
||||
21 => 'KREL',
|
||||
22 => 'Akció',
|
||||
];
|
||||
|
||||
private array $excelFieldPointer = [
|
||||
@ -132,7 +131,6 @@ public function getExcelFieldPointer(): array
|
||||
'buyerProductName' => 19,
|
||||
'note' => 20,
|
||||
'krel' => 21,
|
||||
'specialOffer' => 22,
|
||||
|
||||
];
|
||||
|
||||
@ -159,7 +157,6 @@ public function getExcelFieldPointer(): array
|
||||
'buyerProductName' => ['string'], // 19
|
||||
'note' => ['string'], // 20
|
||||
'krel' => ['string'], // 21
|
||||
'specialOffer' => ['string'], // 22
|
||||
];
|
||||
|
||||
private array $excelFieldConverter = [
|
||||
@ -168,7 +165,6 @@ public function getExcelFieldPointer(): array
|
||||
'vat' => ['percentage'],
|
||||
'note' => ['null'],
|
||||
'krel' => ['booleanCustom'],
|
||||
'specialOffer' => ['booleanCustom'],
|
||||
'hooreycaId' => ['null'],
|
||||
'HooreycaUnit' => ['null'],
|
||||
'HooreycaMultiplier' => ['null'],
|
||||
|
||||
@ -187,15 +187,6 @@ public function updateStepStatus(
|
||||
private const HEADER_ROW = 3;
|
||||
private const DATA_START_ROW = 5;
|
||||
|
||||
/**
|
||||
* Opcionális (visszafelé kompatibilis) oszlopfejlécek indexei.
|
||||
* Ha egy ilyen oszlop hiányzik a 3. sorból, az nem blokkoló hiba, csak figyelmeztetés,
|
||||
* és a hozzá tartozó feldolgozás (pl. akciós jelölés) kimarad.
|
||||
*/
|
||||
private const OPTIONAL_HEADER_INDEXES = [
|
||||
22, // Akció (specialOffer)
|
||||
];
|
||||
|
||||
/**
|
||||
* Előfeldolgozás (Pre-processing) - Strukturális és technikai ellenőrzés
|
||||
*
|
||||
@ -393,12 +384,6 @@ private function buildColumnMap(Worksheet $worksheet): array
|
||||
if (isset($headerByName[$normalizedExpected])) {
|
||||
$map[$index] = $headerByName[$normalizedExpected];
|
||||
$usedColumns[$headerByName[$normalizedExpected]] = true;
|
||||
} elseif (in_array($index, self::OPTIONAL_HEADER_INDEXES, true)) {
|
||||
// Opcionális oszlop hiánya nem blokkoló, csak figyelmeztetés (visszafelé kompatibilitás)
|
||||
$warnings[] = $this->buildWarning(
|
||||
PreProcessErrorCode::COLUMN_MISSING,
|
||||
"Hiányzó opcionális oszlopfejléc: '{$expectedLabel}' (a " . self::HEADER_ROW . ". sorban nem található); a feldolgozás e nélkül folytatódik."
|
||||
);
|
||||
} else {
|
||||
// Hiányzó kötelező fejléc: blokkoló hiba
|
||||
$errors[] = $this->buildError(
|
||||
@ -641,9 +626,7 @@ public function validate(PricelistFile $pricelistFile): bool
|
||||
PricelistFileLine::insert($dataToInsert);
|
||||
}
|
||||
|
||||
// Az "Akció" (specialOffer) opcionális oszlop: csak akkor kezeljük, ha ténylegesen szerepel a fájlban
|
||||
$hasSpecialOfferColumn = isset($columnMap[22]);
|
||||
$this->runBusinessValidation($pricelistFile, $hasSpecialOfferColumn);
|
||||
$this->runBusinessValidation($pricelistFile);
|
||||
|
||||
$duration = round(microtime(true) - $startTime, 2);
|
||||
|
||||
@ -914,7 +897,7 @@ protected function validateProducer(array $payload, array $producerLookup): ?str
|
||||
*
|
||||
* @param PricelistFile $pricelistFile
|
||||
*/
|
||||
protected function runBusinessValidation(PricelistFile $pricelistFile, bool $hasSpecialOfferColumn = false): void
|
||||
protected function runBusinessValidation(PricelistFile $pricelistFile): void
|
||||
{
|
||||
$this->updateStepStatus($pricelistFile, PricelistWorkflowStep::Validation, 'inprogress', 'Üzleti validálás...', 100);
|
||||
|
||||
@ -925,17 +908,15 @@ protected function runBusinessValidation(PricelistFile $pricelistFile, bool $has
|
||||
$producerIdToName = $this->getProducerIdMap();
|
||||
$groupIdToPath = $this->getProductGroupIdMap();
|
||||
|
||||
$pricelistFile->lines()->chunk(1000, function ($lines) use ($groupLookup, $producerLookup, $productLookup, $producerIdToName, $groupIdToPath, $hasSpecialOfferColumn) {
|
||||
$pricelistFile->lines()->chunk(1000, function ($lines) use ($groupLookup, $producerLookup, $productLookup, $producerIdToName, $groupIdToPath) {
|
||||
$updates = [];
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$payload = $line->payload;
|
||||
$validationMessages = [];
|
||||
|
||||
// 1. Kötelező mezők ellenőrzése (0-2, 4-12 és 14)
|
||||
// Megjegyzés: az "Alcsoport 2" (3. index) NEM kötelező, mivel vannak
|
||||
// olyan termékek, amelyek csak az "Alcsoport 1" szinthez vannak rendelve.
|
||||
$mandatoryIndexes = [0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14];
|
||||
// 1. Kötelező mezők ellenőrzése (0-12 és 14)
|
||||
$mandatoryIndexes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14];
|
||||
foreach ($mandatoryIndexes as $mIndex) {
|
||||
$mLabel = PriceListService::EXPECTED_HEADERS[$mIndex];
|
||||
$mValue = $payload[$mLabel] ?? null;
|
||||
@ -1135,25 +1116,6 @@ protected function runBusinessValidation(PricelistFile $pricelistFile, bool $has
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Akciós (specialOffer) módosulás - csak ha az "Akció" oszlop szerepel a fájlban.
|
||||
// booleanCustom logika: üres érték => false, bármilyen más érték => true.
|
||||
if ($hasSpecialOfferColumn) {
|
||||
$specialOfferRaw = $payload[PriceListService::EXPECTED_HEADERS[22]] ?? null;
|
||||
$newSpecialOffer = strlen(trim((string)$specialOfferRaw)) > 0;
|
||||
$oldSpecialOffer = (bool)($existingProduct['specialOffer'] ?? false);
|
||||
|
||||
if ($newSpecialOffer !== $oldSpecialOffer) {
|
||||
$isUpdated = true;
|
||||
$diff['specialOffer'] = [
|
||||
'old' => (int)$oldSpecialOffer,
|
||||
'new' => (int)$newSpecialOffer,
|
||||
'old_label' => $oldSpecialOffer ? 'Akciós' : 'Nem akciós',
|
||||
'new_label' => $newSpecialOffer ? 'Akciós' : 'Nem akciós',
|
||||
'label' => PriceListService::EXPECTED_HEADERS[22],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$updates[] = [
|
||||
|
||||
@ -18,7 +18,6 @@
|
||||
"laravel-json-api/laravel": "^5.1",
|
||||
"laravel-lang/lang": "^15.0",
|
||||
"laravel/framework": "^12.48",
|
||||
"laravel/pennant": "^1.24",
|
||||
"laravel/sanctum": "^4.0",
|
||||
"laravel/tinker": "^2.10.1",
|
||||
"league/flysystem-aws-s3-v3": "^3.0",
|
||||
|
||||
79
composer.lock
generated
79
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "e6b0d54f37a81c8c859c977ad5d3527c",
|
||||
"content-hash": "05f09a61a6722503ee896c57c530bf6b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "ahmedhakeem/extra",
|
||||
@ -5084,83 +5084,6 @@
|
||||
},
|
||||
"time": "2026-03-26T14:51:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/pennant",
|
||||
"version": "v1.24.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/pennant.git",
|
||||
"reference": "b99fbc8038eb2c0a2642e9e13077a75e892b61d9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/pennant/zipball/b99fbc8038eb2c0a2642e9e13077a75e892b61d9",
|
||||
"reference": "b99fbc8038eb2c0a2642e9e13077a75e892b61d9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/console": "^10.0|^11.0|^12.0|^13.0",
|
||||
"illuminate/container": "^10.0|^11.0|^12.0|^13.0",
|
||||
"illuminate/contracts": "^10.0|^11.0|^12.0|^13.0",
|
||||
"illuminate/database": "^10.0|^11.0|^12.0|^13.0",
|
||||
"illuminate/queue": "^10.0|^11.0|^12.0|^13.0",
|
||||
"illuminate/support": "^10.0|^11.0|^12.0|^13.0",
|
||||
"php": "^8.1",
|
||||
"symfony/console": "^6.0|^7.0|^8.0",
|
||||
"symfony/finder": "^6.0|^7.0|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/octane": "^1.4|^2.0",
|
||||
"orchestra/testbench": "^8.36|^9.15|^10.8|^11.0",
|
||||
"phpstan/phpstan": "^1.10"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"aliases": {
|
||||
"Feature": "Laravel\\Pennant\\Feature"
|
||||
},
|
||||
"providers": [
|
||||
"Laravel\\Pennant\\PennantServiceProvider"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/helpers.php",
|
||||
"src/functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Laravel\\Pennant\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "A simple, lightweight library for managing feature flags.",
|
||||
"homepage": "https://github.com/laravel/pennant",
|
||||
"keywords": [
|
||||
"feature",
|
||||
"flags",
|
||||
"laravel",
|
||||
"pennant"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/pennant/issues",
|
||||
"source": "https://github.com/laravel/pennant"
|
||||
},
|
||||
"time": "2026-06-28T17:14:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/prompts",
|
||||
"version": "v0.3.16",
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Pennant Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you will specify the default store that Pennant should use when
|
||||
| storing and resolving feature flag values. Pennant ships with the
|
||||
| ability to store flag values in an in-memory array or database.
|
||||
|
|
||||
| Supported: "array", "database"
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('PENNANT_STORE', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pennant Stores
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure each of the stores that should be available to
|
||||
| Pennant. These stores shall be used to store resolved feature flag
|
||||
| values - you may configure as many as your application requires.
|
||||
|
|
||||
*/
|
||||
|
||||
'stores' => [
|
||||
|
||||
'array' => [
|
||||
'driver' => 'array',
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'connection' => null,
|
||||
'table' => 'features',
|
||||
],
|
||||
|
||||
],
|
||||
];
|
||||
@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Laravel\Pennant\Migrations\PennantMigration;
|
||||
|
||||
return new class extends PennantMigration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('features', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('scope');
|
||||
$table->text('value');
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['name', 'scope']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('features');
|
||||
}
|
||||
};
|
||||
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('feature_flags', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name')->unique();
|
||||
$table->string('label');
|
||||
$table->text('description')->nullable();
|
||||
$table->boolean('enabled')->default(true);
|
||||
$table->json('stages')->nullable();
|
||||
$table->json('roles')->nullable();
|
||||
|
||||
// Audit mezők a BaseAuditable-hoz
|
||||
$table->unsignedBigInteger('created_by')->nullable();
|
||||
$table->unsignedBigInteger('updated_by')->nullable();
|
||||
$table->unsignedBigInteger('deleted_by')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('feature_flags');
|
||||
}
|
||||
};
|
||||
@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('feature_flag_overrides', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('feature_flag_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->boolean('active');
|
||||
|
||||
// Audit mezők a BaseAuditable-hoz
|
||||
$table->unsignedBigInteger('created_by')->nullable();
|
||||
$table->unsignedBigInteger('updated_by')->nullable();
|
||||
$table->unsignedBigInteger('deleted_by')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['feature_flag_id', 'user_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('feature_flag_overrides');
|
||||
}
|
||||
};
|
||||
@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('suppliers', function (Blueprint $table) {
|
||||
$table->boolean('hasCustomerCode')->default(false)->after('deliveryLeadTime');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('suppliers', function (Blueprint $table) {
|
||||
$table->dropColumn('hasCustomerCode');
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('profit_center_supplier_codes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('profit_center_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('supplier_id')->constrained()->onDelete('cascade');
|
||||
$table->string('customerCode')->nullable();
|
||||
$table->userIdFields();
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->unique(['profit_center_id', 'supplier_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('profit_center_supplier_codes');
|
||||
}
|
||||
};
|
||||
@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\FeatureFlag;
|
||||
use App\Models\Role;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class FeatureFlagSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* A "developer" role és a "FeatureFlagAdmin" flag nélkül a feature flag
|
||||
* admin felület (/admin/feature-flags) senki számára nem elérhető - a
|
||||
* FeatureFlagPolicy/FeatureFlagOverridePolicy ezt a flaget nézi, egy
|
||||
* ismeretlen (még nem seedelt) flag pedig mindenkinek inaktívnak számít.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
Role::firstOrCreate(
|
||||
['name' => 'developer'],
|
||||
['display_name' => 'Developer', 'description' => 'Fejlesztői jogosultság'],
|
||||
);
|
||||
|
||||
FeatureFlag::updateOrCreate(
|
||||
['name' => 'FeatureFlagAdmin'],
|
||||
[
|
||||
'label' => 'Feature flag admin felület',
|
||||
'description' => 'A feature flag admin felület (/admin/feature-flags) elérését szabályozza. Enélkül senki nem éri el a felületet.',
|
||||
'enabled' => true,
|
||||
'stages' => null,
|
||||
'roles' => ['developer'],
|
||||
],
|
||||
);
|
||||
|
||||
FeatureFlag::updateOrCreate(
|
||||
['name' => 'SupplierCustomerCode'],
|
||||
[
|
||||
'label' => 'Beszállítói vevőkód (profitcenterenként)',
|
||||
'description' => 'A profitcenterenkénti beszállítói vevőkód kezelését szabályozza a legacy és modern felületen a fejlesztés/rollout alatt. Átmeneti kapcsoló, a funkció véglegesítése után eltávolítható.',
|
||||
// enabled=true + roles=['developer'] a FeatureFlagAdmin mintáját követi: az "enabled"
|
||||
// egy globális kapcsoló (false esetén roles-tól függetlenül SENKI nem látja), tehát a
|
||||
// "csak developer lássa rollout alatt" állapotot enabled=true + roles szűkítéssel kell
|
||||
// kifejezni, nem enabled=false-fal (az utóbbi a developer usereket is kizárja).
|
||||
'enabled' => true,
|
||||
'stages' => null,
|
||||
'roles' => ['developer'],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -82,9 +82,6 @@ window.PriceListProfitCenterAdmin=function (options) {
|
||||
$('.sidebar-menu .sidebar-dropdown .sidebar-submenu li a.categoryItem.clickable').on('click',
|
||||
function () {
|
||||
let groupId=$(this).data('id');
|
||||
if (groupId === 'all') {
|
||||
groupId = null;
|
||||
}
|
||||
selectedProductGroup=groupId;
|
||||
root.setDatatableHeaderText(root.getTreePathName(this));
|
||||
/*
|
||||
@ -94,12 +91,7 @@ window.PriceListProfitCenterAdmin=function (options) {
|
||||
root.refreshDatatable();
|
||||
});
|
||||
|
||||
$('.sidebar-wrapper a.categoryItem.categoryParent').off('click');
|
||||
$('.sidebar-wrapper a.categoryItem.categoryParent').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
$(this).next('ul.subgroup-list').slideToggle(200);
|
||||
$(this).toggleClass('open');
|
||||
});
|
||||
|
||||
}
|
||||
this.refreshProductGroup=function (){
|
||||
let ajaxData={
|
||||
|
||||
@ -18,9 +18,6 @@ if(typeof Supplier==='undefined'){
|
||||
if(typeof this.hasDeliveryConstraint ==="undefined"){
|
||||
this.hasDeliveryConstraint=0;
|
||||
}
|
||||
if(typeof this.hasCustomerCode ==="undefined"){
|
||||
this.hasCustomerCode=0;
|
||||
}
|
||||
if(typeof this.orderCutOffTime ==="undefined"){
|
||||
this.orderCutOffTime=12;
|
||||
}
|
||||
@ -232,7 +229,6 @@ window.SupplierAdmin=function (options,SupplierItem) {
|
||||
$(CSSSelectorForm+' *[data-toggle=canSeeInOrder][data-title='+root.Supplier.canSeeInOrder+']').click();
|
||||
$(CSSSelectorForm+' *[data-toggle=hooreycaDataActive][data-title='+root.Supplier.hooreycaDataActive+']').click();
|
||||
$(CSSSelectorForm+' *[data-toggle=hasDeliveryConstraint][data-title='+root.Supplier.hasDeliveryConstraint+']').click();
|
||||
$(CSSSelectorForm+' *[data-toggle=hasCustomerCode][data-title='+root.Supplier.hasCustomerCode+']').click();
|
||||
$(CSSSelectorForm+' input[name=orderCutOffTime]').val(root.Supplier.orderCutOffTime);
|
||||
$(CSSSelectorForm+' select[name=deliveryLeadTime]').val(root.Supplier.deliveryLeadTime);
|
||||
let productGroup=[];
|
||||
|
||||
@ -463,22 +463,6 @@
|
||||
|
||||
}
|
||||
|
||||
.sidebar-wrapper .sidebar-menu .sidebar-dropdown .sidebar-submenu li a.categoryItem.categoryParent {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
.sidebar-wrapper .sidebar-menu .sidebar-dropdown .sidebar-submenu li a.categoryItem.categoryParent:after {
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-weight: 900;
|
||||
content: "\f105"; /* jobbra mutató nyíl */
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
.sidebar-wrapper .sidebar-menu .sidebar-dropdown .sidebar-submenu li a.categoryItem.categoryParent.open:after {
|
||||
transform: rotate(90deg); /* nyitott állapotban lefelé fordul */
|
||||
}
|
||||
|
||||
/*--------------------------side-footer------------------------------*/
|
||||
|
||||
.sidebar-footer {
|
||||
@ -668,31 +652,6 @@
|
||||
color: #b8bfce;
|
||||
}
|
||||
|
||||
/*----- rugalmas magasság: a Csoportok tölti ki a szabad helyet -----*/
|
||||
.sidebar-wrapper .sidebar-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.sidebar-wrapper .sidebar-menu,
|
||||
.sidebar-wrapper .sidebar-menu > ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0; /* flexben a görgetés feltétele */
|
||||
}
|
||||
/* a Csoportok li nőjjon, a többi (Kosár, Extra) megtartja a méretét */
|
||||
.sidebar-wrapper .sidebar-menu .sidebar-dropdown.active:not(.extra-block) {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.sidebar-wrapper .sidebar-menu .sidebar-dropdown.active:not(.extra-block) .sidebar-submenu {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@ -350,20 +350,6 @@ class="form-control" id="openHours"
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@feature('SupplierCustomerCode')
|
||||
<div class="form-group row">
|
||||
<label for="hasCustomerCode" class="col-sm-2 col-form-label">Vevőkód funkció:</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<div class="btn-group customRadioBtnGroup">
|
||||
<a class="btn btn-primary btn-sm notActive" data-toggle="hasCustomerCode" data-title="1">Igen</a>
|
||||
<a class="btn btn-primary btn-sm active" data-toggle="hasCustomerCode" data-title="0">Nem</a>
|
||||
</div>
|
||||
<input type="hidden" name="hasCustomerCode" value="0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endfeature
|
||||
<div class="form-group row">
|
||||
<label for="hasDeliveryConstraint" class="col-sm-2 col-form-label">Szállítási korlátozások:</label>
|
||||
<div class="col-sm-10">
|
||||
|
||||
@ -82,7 +82,7 @@
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-05-17 11:53";
|
||||
@endphp
|
||||
@ -102,7 +102,7 @@
|
||||
|
||||
{{--
|
||||
This is displayed
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-09-30 11:14";
|
||||
|
||||
@ -123,7 +123,7 @@
|
||||
<hr>
|
||||
<p class="lead">ChangeLog </p>
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-10-17 10:51";
|
||||
|
||||
@ -135,7 +135,7 @@
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-10-15 19:11";
|
||||
@endphp
|
||||
@ -146,7 +146,7 @@
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-10-04 12:45";
|
||||
|
||||
@ -161,7 +161,7 @@
|
||||
@endif
|
||||
|
||||
|
||||
@if (config('app.stage')=='PROD')
|
||||
@if (env('APP_STAGE')=='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-09-30 09:02";
|
||||
@endphp
|
||||
@ -179,7 +179,7 @@
|
||||
@endif
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-09-13 19:32";
|
||||
|
||||
@ -190,7 +190,7 @@
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-09-10 10:21";
|
||||
|
||||
@ -208,7 +208,7 @@
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
@if (config('app.stage')=='PROD')
|
||||
@if (env('APP_STAGE')=='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-06-18 17:20";
|
||||
|
||||
@ -236,7 +236,7 @@
|
||||
@endif
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-06-18 17:20";
|
||||
|
||||
@ -249,7 +249,7 @@
|
||||
@endif
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-06-09 08:10";
|
||||
@endphp
|
||||
@ -261,7 +261,7 @@
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-06-08 09:22";
|
||||
@endphp
|
||||
@ -275,7 +275,7 @@
|
||||
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-05-15 13:10";
|
||||
@endphp
|
||||
@ -287,7 +287,7 @@
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-05-04 11:17";
|
||||
@endphp
|
||||
@ -298,7 +298,7 @@
|
||||
@endif
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-04-26 07:28";
|
||||
@endphp
|
||||
@ -309,7 +309,7 @@
|
||||
@endif
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-04-26 05:51";
|
||||
@endphp
|
||||
@ -322,7 +322,7 @@
|
||||
@endif
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-03-30 19:41";
|
||||
@endphp
|
||||
@ -333,7 +333,7 @@
|
||||
@endif
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-03-29 18:01";
|
||||
@endphp
|
||||
@ -344,7 +344,7 @@
|
||||
@endif
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-03-28 23:12";
|
||||
@endphp
|
||||
@ -382,7 +382,7 @@
|
||||
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-02-19 20:01";
|
||||
@endphp
|
||||
@ -397,7 +397,7 @@
|
||||
@endif
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-02-15 08:12";
|
||||
@endphp
|
||||
@ -412,7 +412,7 @@
|
||||
@endphp
|
||||
@endif
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2023-01-30 05:53";
|
||||
@endphp
|
||||
@ -426,7 +426,7 @@
|
||||
@endphp
|
||||
@endif
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-11-22 13:45";
|
||||
@endphp
|
||||
@ -442,7 +442,7 @@
|
||||
<li>NEW "Megrendelések szűrő"<a class="pmLink" href="http://pm.e98.hu/issue/EV3-63">[EV3-63]</a></li>
|
||||
</ul>
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-11-02 07:12";
|
||||
@endphp
|
||||
@ -458,7 +458,7 @@
|
||||
<li>CHANGE Termékek kiválasztásánál oszlopok szélességének átállítása</li>
|
||||
</ul>
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-10-06 10:58";
|
||||
@endphp
|
||||
@ -472,7 +472,7 @@
|
||||
<li>NEW "Legkisebb eladási egység - mennyiségi egység"<a class="pmLink" href="http://pm.e98.hu/issue/EV3-62">[EV3-62]</a></li>
|
||||
</ul>
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-07-06 07:58";
|
||||
@endphp
|
||||
@ -486,7 +486,7 @@
|
||||
<li>CHANGE,FIX "Új termékcsoport felvétel"<a class="pmLink" href="http://pm.e98.hu/issue/EV3-59">[EV3-59]</a></li>
|
||||
</ul>
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-06-20 06:19";
|
||||
@endphp
|
||||
@ -508,7 +508,7 @@
|
||||
@endif
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-06-08 11:35";
|
||||
@endphp
|
||||
@ -522,7 +522,7 @@
|
||||
@endphp
|
||||
@endif
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-06-01 06:11";
|
||||
@endphp
|
||||
@ -543,7 +543,7 @@
|
||||
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-05-17 06:32";
|
||||
@endphp
|
||||
@ -559,7 +559,7 @@
|
||||
|
||||
</ul>
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-05-12 09:45";
|
||||
@endphp
|
||||
@ -580,7 +580,7 @@
|
||||
|
||||
@endif
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-05-10 11:53";
|
||||
@endphp
|
||||
@ -603,7 +603,7 @@
|
||||
<li>FIX "Megrendelés visszaigazolás időpontja"<a class="pmLink" href="http://pm.e98.hu/issue/EV3-50">[EV3-50]</a></li>
|
||||
</ul>
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-04-11 06:53";
|
||||
@endphp
|
||||
@ -620,7 +620,7 @@
|
||||
<li>NEW PART "Beszállító megjegyzés rovat"<a class="pmLink" href="http://pm.e98.hu/issue/EV3-46">[EV3-46]</a></li>
|
||||
</ul>
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-03-31 12:30";
|
||||
@endphp
|
||||
@ -640,7 +640,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-03-25 05:57";
|
||||
@endphp
|
||||
@ -672,7 +672,7 @@
|
||||
</ul>
|
||||
|
||||
|
||||
@if (config('app.stage')!='PROD')
|
||||
@if (env('APP_STAGE')!='PROD')
|
||||
@php
|
||||
$featureStartTime="2022-03-09 07:30";
|
||||
@endphp
|
||||
|
||||
@ -1,17 +1,4 @@
|
||||
@php
|
||||
$deliveryChildren = [
|
||||
['displayName' => 'Szállítási sablonok', 'link' => \App\Filament\Resources\DeliverySchedules\DeliveryScheduleResource::getUrl()],
|
||||
['displayName' => 'Beszállítói felülbírálások', 'link' => \App\Filament\Resources\DeliveryCalendarOverrides\DeliveryCalendarOverrideResource::getUrl()],
|
||||
['displayName' => 'Profitcenter ütemezés', 'link' => \App\Filament\Resources\ProfitCenterSupplierSchedules\ProfitCenterSupplierScheduleResource::getUrl()],
|
||||
['displayName' => 'Munkanaptárak', 'link' => \App\Filament\Resources\WorkCalendars\WorkCalendarResource::getUrl()],
|
||||
];
|
||||
|
||||
// A vevőkód-kezelő oldalt a Szállítás menü utolsó elemeként jelenítjük meg,
|
||||
// a SupplierCustomerCode feature flagre bízva a láthatóságot (rollout-védelem).
|
||||
if (\App\Filament\Pages\SupplierCustomerCodes::canAccess()) {
|
||||
$deliveryChildren[] = ['displayName' => 'Beszállítói vevőkódok', 'link' => \App\Filament\Pages\SupplierCustomerCodes::getUrl()];
|
||||
}
|
||||
|
||||
$modules = [
|
||||
['name' => 'home', 'displayName' => 'Főoldal', 'icon' => 'home', 'link' => route('legacy.show', ['path' => 'dashboard']), 'roles' => ['root','admin','developer','profit-center']],
|
||||
['name' => 'order', 'displayName' => 'Új megrendelés', 'icon' => 'bevetelezes', 'link' => route('legacy.show', ['path' => 'order']), 'roles' => ['root','profit-center']],
|
||||
@ -26,20 +13,14 @@
|
||||
['name' => 'statistics', 'displayName' => 'Statisztikák', 'icon' => 'kimutatasok', 'link' => route('legacy.show', ['path' => 'admin/statistics']), 'roles' => ['root','developer','admin']],
|
||||
['name' => 'export', 'displayName' => 'Export', 'icon' => 'export', 'link' => route('legacy.show', ['path' => 'admin/export']), 'roles' => ['root','developer','admin']],
|
||||
['name' => 'priceListProcessor', 'displayName' => 'Árlista feldolgozó', 'icon' => 'price_list', 'link' => '/admin/pricelist-files', 'roles' => ['root','developer','admin']],
|
||||
['name' => 'delivery', 'displayName' => 'Szállítás', 'icon' => 'timetable3', 'roles' => ['root','admin','developer'], 'children' => $deliveryChildren],
|
||||
['name' => 'delivery', 'displayName' => 'Szállítás', 'icon' => 'timetable3', 'roles' => ['root','admin','developer'], 'children' => [
|
||||
['displayName' => 'Szállítási sablonok', 'link' => \App\Filament\Resources\DeliverySchedules\DeliveryScheduleResource::getUrl()],
|
||||
['displayName' => 'Beszállítói felülbírálások', 'link' => \App\Filament\Resources\DeliveryCalendarOverrides\DeliveryCalendarOverrideResource::getUrl()],
|
||||
['displayName' => 'Profitcenter ütemezés', 'link' => \App\Filament\Resources\ProfitCenterSupplierSchedules\ProfitCenterSupplierScheduleResource::getUrl()],
|
||||
['displayName' => 'Munkanaptárak', 'link' => \App\Filament\Resources\WorkCalendars\WorkCalendarResource::getUrl()],
|
||||
]],
|
||||
['name' => 'calendarTest', 'displayName' => 'Naptár Teszt', 'icon' => 'idopontok', 'link' => \App\Filament\Pages\CalendarTest::getUrl(), 'roles' => ['root','developer','admin']],
|
||||
];
|
||||
|
||||
// A feature flag admin felület megjelenítését nem statikus role-listával,
|
||||
// hanem a FeatureFlagResource jogosultság-ellenőrzésével (FeatureFlagPolicy,
|
||||
// ami a "FeatureFlagAdmin" flaget nézi) döntjük el - így a hozzáférés
|
||||
// deploy nélkül, az admin felületről szabályozható.
|
||||
if (\App\Filament\Resources\FeatureFlags\FeatureFlagResource::canViewAny()) {
|
||||
$modules[] = ['name' => 'featureFlags', 'displayName' => 'Feature flagek', 'icon' => 'setup', 'roles' => ['root','developer','admin','profit-center'], 'children' => [
|
||||
['displayName' => 'Flagek', 'link' => \App\Filament\Resources\FeatureFlags\FeatureFlagResource::getUrl()],
|
||||
['displayName' => 'Egyéni felülbírálások', 'link' => \App\Filament\Resources\FeatureFlagOverrides\FeatureFlagOverrideResource::getUrl()],
|
||||
]];
|
||||
}
|
||||
@endphp
|
||||
|
||||
<nav class="fi-speed-button-nav flex items-center h-full ml-4 w-full">
|
||||
@ -104,8 +85,8 @@ class="w-16 h-16 object-contain mb-1"
|
||||
@endrole
|
||||
@endforeach
|
||||
</ul>
|
||||
@if (config('app.stage', 'DEV') != 'PROD')
|
||||
<h2 class="text-[#b83400] font-bold text-lg ml-4">site:{{ config('app.stage', 'DEV') }}</h2>
|
||||
@if (env('APP_STAGE', 'DEV') != 'PROD')
|
||||
<h2 class="text-[#b83400] font-bold text-lg ml-4">site:{{ env('APP_STAGE', 'DEV') }}</h2>
|
||||
@endif
|
||||
<div class="ml-auto flex items-center pr-2">
|
||||
<img style="max-width: none; height: 59px; vertical-align: middle; margin-top: 20px;"
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
<x-filament-panels::page>
|
||||
<div class="space-y-6">
|
||||
<div class="p-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700">
|
||||
<h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">Beszállítói vevőkódok</h5>
|
||||
<p class="mb-3 font-normal text-gray-700 dark:text-gray-400">Válassza ki a beszállítót, majd adja meg a profitcenterenkénti vevőkódokat.</p>
|
||||
</div>
|
||||
|
||||
<div class="p-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700">
|
||||
<form wire:submit="save">
|
||||
{{ $this->form }}
|
||||
|
||||
<div class="mt-6">
|
||||
<x-filament::actions
|
||||
:actions="$this->getFormActions()"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</x-filament-panels::page>
|
||||
@ -13,19 +13,6 @@
|
||||
$priceListRoute=route('priceList.index');
|
||||
$supplierRoute=route('supplier.index');
|
||||
};
|
||||
$deliveryChildren=[
|
||||
['DisplayName' => 'Szállítási sablonok', 'link' => '/admin/delivery-schedules'],
|
||||
['DisplayName' => 'Beszállítói felülbírálások', 'link' => '/admin/delivery-calendar-overrides'],
|
||||
['DisplayName' => 'Profitcenter ütemezés', 'link' => '/admin/profit-center-supplier-schedules'],
|
||||
['DisplayName' => 'Munkanaptárak', 'link' => '/admin/work-calendars'],
|
||||
['DisplayName' => 'Naptár teszt', 'link' => '/admin/calendar-test'],
|
||||
];
|
||||
// A vevőkód-kezelő oldalt a Szállítás menü utolsó elemeként jelenítjük meg,
|
||||
// a SupplierCustomerCode feature flagre bízva a láthatóságot (rollout-védelem) -
|
||||
// ugyanaz a canAccess() hívás, mint a modern speed-button-nav-bar.blade.php-ban.
|
||||
if (\App\Filament\Pages\SupplierCustomerCodes::canAccess()) {
|
||||
$deliveryChildren[] = ['DisplayName' => 'Beszállítói vevőkódok', 'link' => \App\Filament\Pages\SupplierCustomerCodes::getUrl()];
|
||||
}
|
||||
$Modules=[
|
||||
'home'=>[
|
||||
'name'=>'home',
|
||||
@ -134,7 +121,7 @@
|
||||
'icon' => 'price_list',
|
||||
'link' => '/admin/pricelist-files',
|
||||
'noAjax' => true,
|
||||
'roles' => ['developer','admin'],
|
||||
'roles' => ['developer'],
|
||||
],
|
||||
'delivery' => [
|
||||
'name' => 'delivery',
|
||||
@ -143,7 +130,13 @@
|
||||
'link' => '#',
|
||||
'roles' => ['root', 'admin', 'developer'],
|
||||
'noAjax' => true,
|
||||
'children' => $deliveryChildren,
|
||||
'children' => [
|
||||
['DisplayName' => 'Szállítási sablonok', 'link' => '/admin/delivery-schedules'],
|
||||
['DisplayName' => 'Beszállítói felülbírálások', 'link' => '/admin/delivery-calendar-overrides'],
|
||||
['DisplayName' => 'Profitcenter ütemezés', 'link' => '/admin/profit-center-supplier-schedules'],
|
||||
['DisplayName' => 'Munkanaptárak', 'link' => '/admin/work-calendars'],
|
||||
['DisplayName' => 'Naptár teszt', 'link' => '/admin/calendar-test']
|
||||
],
|
||||
],
|
||||
/*
|
||||
'calendarTest' => [
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
// auth()->loginUsingId(1);
|
||||
// auth()->logout();
|
||||
|
||||
if (in_array(strtolower(config('app.stage')), ['d2dtst', 'e2etst'])) {
|
||||
if (in_array(strtolower(env('APP_STAGE')), ['d2dtst', 'e2etst'])) {
|
||||
echo view('layout.wherehere')->render();
|
||||
exit();
|
||||
}
|
||||
@ -38,7 +38,7 @@
|
||||
]);
|
||||
});
|
||||
/*
|
||||
if (! in_array(strtolower(config('app.stage')), ['d2dtst', 'e2etst', 'dev','local'])) {
|
||||
if (! in_array(strtolower(env('APP_STAGE')), ['d2dtst', 'e2etst', 'dev','local'])) {
|
||||
URL::forceScheme('https');
|
||||
} else {
|
||||
URL::forceScheme('http');
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Filament\Resources\FeatureFlags\FeatureFlagResource;
|
||||
use App\Filament\Resources\FeatureFlags\Pages\ListFeatureFlags;
|
||||
use App\Models\FeatureFlag;
|
||||
use App\Models\Role;
|
||||
use App\Models\User;
|
||||
use App\Services\FeatureFlagRegistrar;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Livewire\Livewire;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class, RefreshDatabase::class);
|
||||
|
||||
test('a lista oldal hiba nélkül renderel, ha egy flagnek van szerepköre és stage-e', function () {
|
||||
$devRole = Role::create(['name' => 'developer', 'display_name' => 'Developer']);
|
||||
$developer = User::factory()->create();
|
||||
$developer->addRole($devRole);
|
||||
|
||||
FeatureFlag::create([
|
||||
'name' => 'FeatureFlagAdmin',
|
||||
'label' => 'Feature flag admin felület',
|
||||
'enabled' => true,
|
||||
'stages' => null,
|
||||
'roles' => ['developer'],
|
||||
]);
|
||||
FeatureFlag::create([
|
||||
'name' => 'ui-test-flag',
|
||||
'label' => 'UI teszt flag',
|
||||
'enabled' => true,
|
||||
'stages' => ['TEST'],
|
||||
'roles' => ['developer'],
|
||||
]);
|
||||
app(FeatureFlagRegistrar::class)->registerAll();
|
||||
|
||||
$this->actingAs($developer);
|
||||
|
||||
Livewire::test(ListFeatureFlags::class)
|
||||
->assertSuccessful();
|
||||
});
|
||||
|
||||
test('csak a FeatureFlagAdmin flaggel rendelkező szerepkör éri el a feature flag admin felületet', function () {
|
||||
$adminRole = Role::create(['name' => 'admin', 'display_name' => 'Admin']);
|
||||
$devRole = Role::create(['name' => 'developer', 'display_name' => 'Developer']);
|
||||
|
||||
$admin = User::factory()->create();
|
||||
$admin->addRole($adminRole);
|
||||
|
||||
$developer = User::factory()->create();
|
||||
$developer->addRole($devRole);
|
||||
|
||||
FeatureFlag::create([
|
||||
'name' => 'FeatureFlagAdmin',
|
||||
'label' => 'Feature flag admin felület',
|
||||
'enabled' => true,
|
||||
'stages' => null,
|
||||
'roles' => ['developer'],
|
||||
]);
|
||||
app(FeatureFlagRegistrar::class)->registerAll();
|
||||
|
||||
expect(Gate::forUser($admin)->allows('viewAny', FeatureFlag::class))->toBeFalse();
|
||||
expect(Gate::forUser($developer)->allows('viewAny', FeatureFlag::class))->toBeTrue();
|
||||
|
||||
$this->actingAs($admin);
|
||||
$this->get(FeatureFlagResource::getUrl())->assertForbidden();
|
||||
|
||||
$this->actingAs($developer);
|
||||
$this->get(FeatureFlagResource::getUrl())->assertSuccessful();
|
||||
});
|
||||
@ -1,103 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Filament\Resources\FeatureFlagOverrides\FeatureFlagOverrideResource;
|
||||
use App\Filament\Resources\FeatureFlagOverrides\Pages\ListFeatureFlagOverrides;
|
||||
use App\Filament\Resources\FeatureFlags\Pages\EditFeatureFlag;
|
||||
use App\Models\FeatureFlag;
|
||||
use App\Models\FeatureFlagOverride;
|
||||
use App\Models\Role;
|
||||
use App\Models\User;
|
||||
use App\Services\FeatureFlagRegistrar;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Laravel\Pennant\Feature;
|
||||
use Livewire\Livewire;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class, RefreshDatabase::class);
|
||||
|
||||
function actAsDeveloper(): User
|
||||
{
|
||||
$devRole = Role::firstOrCreate(['name' => 'developer'], ['display_name' => 'Developer']);
|
||||
$developer = User::factory()->create();
|
||||
$developer->addRole($devRole);
|
||||
|
||||
FeatureFlag::firstOrCreate(
|
||||
['name' => 'FeatureFlagAdmin'],
|
||||
['label' => 'Feature flag admin felület', 'enabled' => true, 'stages' => null, 'roles' => ['developer']],
|
||||
);
|
||||
app(FeatureFlagRegistrar::class)->registerAll();
|
||||
|
||||
return $developer;
|
||||
}
|
||||
|
||||
test('a flag szerkesztő oldalán megjelenik az egyéni felülbírálások relation manager', function () {
|
||||
$developer = actAsDeveloper();
|
||||
|
||||
$flag = FeatureFlag::create([
|
||||
'name' => 'rm-test-flag',
|
||||
'label' => 'RM teszt flag',
|
||||
'enabled' => true,
|
||||
]);
|
||||
app(FeatureFlagRegistrar::class)->registerAll();
|
||||
|
||||
$targetUser = User::factory()->create();
|
||||
FeatureFlagOverride::create([
|
||||
'feature_flag_id' => $flag->id,
|
||||
'user_id' => $targetUser->id,
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$this->actingAs($developer);
|
||||
|
||||
Livewire::test(EditFeatureFlag::class, ['record' => $flag->getRouteKey()])
|
||||
->assertSuccessful();
|
||||
});
|
||||
|
||||
test('a globális felülbírálás lista renderel, és csak fejlesztők érik el', function () {
|
||||
$developer = actAsDeveloper();
|
||||
|
||||
$adminRole = Role::firstOrCreate(['name' => 'admin'], ['display_name' => 'Admin']);
|
||||
$admin = User::factory()->create();
|
||||
$admin->addRole($adminRole);
|
||||
|
||||
expect(Gate::forUser($admin)->allows('viewAny', FeatureFlagOverride::class))->toBeFalse();
|
||||
expect(Gate::forUser($developer)->allows('viewAny', FeatureFlagOverride::class))->toBeTrue();
|
||||
|
||||
$this->actingAs($admin);
|
||||
$this->get(FeatureFlagOverrideResource::getUrl())->assertForbidden();
|
||||
|
||||
$this->actingAs($developer);
|
||||
Livewire::test(ListFeatureFlagOverrides::class)->assertSuccessful();
|
||||
});
|
||||
|
||||
test('az "örökölt"-re állítás (mass delete) is érvényteleníti a gyorsítótárazott Pennant-feloldást', function () {
|
||||
actAsDeveloper();
|
||||
|
||||
$flag = FeatureFlag::create([
|
||||
'name' => 'purge-test-flag',
|
||||
'label' => 'Purge teszt flag',
|
||||
'enabled' => false,
|
||||
]);
|
||||
app(FeatureFlagRegistrar::class)->registerAll();
|
||||
|
||||
$user = User::factory()->create();
|
||||
|
||||
FeatureFlagOverride::create([
|
||||
'feature_flag_id' => $flag->id,
|
||||
'user_id' => $user->id,
|
||||
'active' => true,
|
||||
]);
|
||||
Feature::for($user)->forget('purge-test-flag');
|
||||
expect(Feature::for($user)->active('purge-test-flag'))->toBeTrue();
|
||||
|
||||
// ugyanaz a mintázat, mint amit a FeatureFlagsTable "Egyéni felülbírálás"
|
||||
// akciója az "örökölt" választásnál használ - model-instance delete()-en
|
||||
// keresztül, hogy a FeatureFlagOverrideObserver kiváltódjon
|
||||
FeatureFlagOverride::where('feature_flag_id', $flag->id)
|
||||
->where('user_id', $user->id)
|
||||
->first()
|
||||
?->delete();
|
||||
|
||||
expect(Feature::for($user)->active('purge-test-flag'))->toBeFalse();
|
||||
});
|
||||
@ -1,123 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Models\FeatureFlag;
|
||||
use App\Models\FeatureFlagOverride;
|
||||
use App\Models\Role;
|
||||
use App\Models\User;
|
||||
use App\Services\FeatureFlagRegistrar;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Laravel\Pennant\Feature;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class, RefreshDatabase::class);
|
||||
|
||||
function defineFlag(array $attributes): FeatureFlag
|
||||
{
|
||||
$flag = FeatureFlag::create(array_merge([
|
||||
'name' => 'test-flag',
|
||||
'label' => 'Test flag',
|
||||
'enabled' => true,
|
||||
'stages' => null,
|
||||
'roles' => null,
|
||||
], $attributes));
|
||||
|
||||
app(FeatureFlagRegistrar::class)->registerAll();
|
||||
|
||||
return $flag;
|
||||
}
|
||||
|
||||
test('a kikapcsolt flag mindenkinek inaktív', function () {
|
||||
defineFlag(['enabled' => false]);
|
||||
|
||||
$user = User::factory()->create();
|
||||
|
||||
expect(Feature::for($user)->active('test-flag'))->toBeFalse();
|
||||
});
|
||||
|
||||
test('a stage-mismatch inaktívvá teszi a flaget', function () {
|
||||
Config::set('app.stage', 'PROD');
|
||||
|
||||
defineFlag(['stages' => ['TEST']]);
|
||||
|
||||
$user = User::factory()->create();
|
||||
|
||||
expect(Feature::for($user)->active('test-flag'))->toBeFalse();
|
||||
});
|
||||
|
||||
test('a stage-match aktívvá teszi a flaget', function () {
|
||||
Config::set('app.stage', 'TEST');
|
||||
|
||||
defineFlag(['stages' => ['TEST']]);
|
||||
|
||||
$user = User::factory()->create();
|
||||
|
||||
expect(Feature::for($user)->active('test-flag'))->toBeTrue();
|
||||
});
|
||||
|
||||
test('a role-mismatch inaktívvá teszi a flaget', function () {
|
||||
Role::create(['name' => 'admin', 'display_name' => 'Admin']);
|
||||
|
||||
defineFlag(['roles' => ['admin']]);
|
||||
|
||||
$user = User::factory()->create();
|
||||
|
||||
expect(Feature::for($user)->active('test-flag'))->toBeFalse();
|
||||
});
|
||||
|
||||
test('a role-match aktívvá teszi a flaget', function () {
|
||||
$role = Role::create(['name' => 'admin', 'display_name' => 'Admin']);
|
||||
|
||||
defineFlag(['roles' => ['admin']]);
|
||||
|
||||
$user = User::factory()->create();
|
||||
$user->addRole($role);
|
||||
|
||||
expect(Feature::for($user)->active('test-flag'))->toBeTrue();
|
||||
});
|
||||
|
||||
test('a user-szintű explicit felülbírálás felülír mindent', function () {
|
||||
$flag = defineFlag(['enabled' => false]);
|
||||
|
||||
$user = User::factory()->create();
|
||||
|
||||
FeatureFlagOverride::create([
|
||||
'feature_flag_id' => $flag->id,
|
||||
'user_id' => $user->id,
|
||||
'active' => true,
|
||||
]);
|
||||
Feature::for($user)->forget('test-flag');
|
||||
|
||||
expect(Feature::for($user)->active('test-flag'))->toBeTrue();
|
||||
});
|
||||
|
||||
test('a flag admin-mentése nem törli az egyéni felülbírálásokat', function () {
|
||||
$flag = defineFlag(['enabled' => true]);
|
||||
|
||||
$user = User::factory()->create();
|
||||
|
||||
FeatureFlagOverride::create([
|
||||
'feature_flag_id' => $flag->id,
|
||||
'user_id' => $user->id,
|
||||
'active' => false,
|
||||
]);
|
||||
Feature::for($user)->forget('test-flag');
|
||||
|
||||
expect(Feature::for($user)->active('test-flag'))->toBeFalse();
|
||||
|
||||
// Admin módosítja a flaget (pl. leírás) - ez kiváltja a FeatureFlagObserver purge-öt
|
||||
$flag->update(['description' => 'módosítva']);
|
||||
app(FeatureFlagRegistrar::class)->registerAll();
|
||||
|
||||
expect(FeatureFlagOverride::where('feature_flag_id', $flag->id)->where('user_id', $user->id)->exists())
|
||||
->toBeTrue();
|
||||
expect(Feature::for($user)->active('test-flag'))->toBeFalse();
|
||||
});
|
||||
|
||||
test('üres stages/roles esetén mindenkinek aktív', function () {
|
||||
defineFlag([]);
|
||||
|
||||
$user = User::factory()->create();
|
||||
|
||||
expect(Feature::for($user)->active('test-flag'))->toBeTrue();
|
||||
});
|
||||
@ -81,41 +81,6 @@ function makeWorksheetWithHeaderOrder(array $order): Worksheet
|
||||
expect($result['map'])->toHaveCount(count($expected));
|
||||
});
|
||||
|
||||
test('a hiányzó opcionális "Akció" oszlop csak figyelmeztetést ad, nem blokkol', function () {
|
||||
$expected = PriceListService::EXPECTED_HEADERS;
|
||||
|
||||
$worksheet = (new Spreadsheet())->getActiveSheet();
|
||||
$col = 1;
|
||||
foreach ($expected as $fieldIndex => $label) {
|
||||
if ($fieldIndex === 22) {
|
||||
// Az "Akció" oszlopot szándékosan kihagyjuk (visszafelé kompatibilis fájl)
|
||||
continue;
|
||||
}
|
||||
$worksheet->setCellValueByColumnAndRow($col, 3, $label);
|
||||
$col++;
|
||||
}
|
||||
|
||||
$result = invokeBuildColumnMap($worksheet);
|
||||
|
||||
$hasBlocker = collect($result['errors'])->contains(fn ($error) => ($error['severity'] ?? '') === 'blocker');
|
||||
expect($hasBlocker)->toBeFalse();
|
||||
expect($result['map'])->not->toHaveKey(22);
|
||||
expect(collect($result['warnings']))->not->toBeEmpty();
|
||||
});
|
||||
|
||||
test('a jelen lévő "Akció" oszlop leképeződik', function () {
|
||||
$expected = PriceListService::EXPECTED_HEADERS;
|
||||
|
||||
$worksheet = makeWorksheetWithHeaderOrder(array_keys($expected));
|
||||
$result = invokeBuildColumnMap($worksheet);
|
||||
|
||||
expect($result['errors'])->toBe([]);
|
||||
expect($result['map'])->toHaveKey(22);
|
||||
|
||||
$value = $worksheet->getCellByColumnAndRow($result['map'][22], 5)->getValue();
|
||||
expect($value)->toBe('VAL_22');
|
||||
});
|
||||
|
||||
test('hiányzó kötelező fejléc blokkoló hibát ad', function () {
|
||||
$expected = PriceListService::EXPECTED_HEADERS;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user