Apply new settings to QR, validation, and import flow

This commit is contained in:
Arya Dwi Putra
2025-12-03 08:49:51 +07:00
parent 4f6dfc5090
commit a2b8376960
2 changed files with 84 additions and 17 deletions

View File

@@ -14,6 +14,7 @@ use App\Models\Unit;
use App\Models\VendorContract;
use App\Models\Warranty;
use App\Services\Asset\AssetService;
use App\Services\System\SystemSettingService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@@ -22,7 +23,10 @@ use Illuminate\Support\Str;
class AssetController extends Controller
{
public function __construct(private readonly AssetService $assets)
public function __construct(
private readonly AssetService $assets,
private readonly SystemSettingService $settings
)
{
}
@@ -209,6 +213,13 @@ class AssetController extends Controller
return back()->withErrors(['file' => 'File tidak bisa dibaca']);
}
$maxRows = (int) $this->settings->get('asset.import_max_rows', 2000);
$allowRemoteImages = (bool) $this->settings->get('asset.import_allow_remote_images', true);
$dedupePriority = $this->settings->get('asset.import_dedupe_priority', 'code');
$defaultStatusName = $this->settings->get('asset.import_default_status');
$defaultCategoryName = $this->settings->get('asset.import_default_category');
$defaultLocationName = $this->settings->get('asset.import_default_location');
// Jika ada ZIP gambar, siapkan mapping filename => binary
$images = [];
if ($request->hasFile('images_zip')) {
@@ -235,9 +246,15 @@ class AssetController extends Controller
$statuses = AssetStatus::pluck('id','name')->toArray();
$categories = AssetCategory::pluck('id','name')->toArray();
$locations = AssetLocation::pluck('id','name')->toArray();
$defaultStatusId = $statuses[$defaultStatusName] ?? null;
$defaultCategoryId = $categories[$defaultCategoryName] ?? null;
$defaultLocationId = $locations[$defaultLocationName] ?? null;
while (($data = fgetcsv($handle)) !== false) {
$line++;
if ($maxRows && ($line-1) > $maxRows) {
break;
}
$row = array_combine($header, $data);
if (!$row) {
$rows[] = ['line' => $line, 'status' => 'error', 'message' => 'Header tidak sesuai', 'data' => $data];
@@ -249,13 +266,23 @@ class AssetController extends Controller
$errors = [];
if (!$name) $errors[] = 'Nama wajib';
$statusId = $statuses[$row['status'] ?? ''] ?? null;
$categoryId = $categories[$row['category'] ?? ''] ?? null;
$locationId = $locations[$row['location'] ?? ''] ?? null;
$statusId = $statuses[$row['status'] ?? ''] ?? $defaultStatusId;
$categoryId = $categories[$row['category'] ?? ''] ?? $defaultCategoryId;
$locationId = $locations[$row['location'] ?? ''] ?? $defaultLocationId;
$targetId = $code && isset($existingCodes[$code]) ? $existingCodes[$code] : null;
if (!$targetId && $serial && isset($existingSerials[$serial])) {
$targetId = $existingSerials[$serial];
$targetId = null;
if ($dedupePriority === 'serial') {
if ($serial && isset($existingSerials[$serial])) {
$targetId = $existingSerials[$serial];
} elseif ($code && isset($existingCodes[$code])) {
$targetId = $existingCodes[$code];
}
} else {
if ($code && isset($existingCodes[$code])) {
$targetId = $existingCodes[$code];
} elseif ($serial && isset($existingSerials[$serial])) {
$targetId = $existingSerials[$serial];
}
}
$rows[] = [
@@ -321,13 +348,15 @@ class AssetController extends Controller
if ($assetModel) {
$binary = null;
if ($imageUrl) {
try {
$resp = \Illuminate\Support\Facades\Http::get($imageUrl);
if ($resp->ok()) {
$binary = $resp->body();
if ($allowRemoteImages) {
try {
$resp = \Illuminate\Support\Facades\Http::get($imageUrl);
if ($resp->ok()) {
$binary = $resp->body();
}
} catch (\Throwable $e) {
// abaikan error download agar import tetap lanjut
}
} catch (\Throwable $e) {
// abaikan error download agar import tetap lanjut
}
} elseif ($imageFile && isset($images[$imageFile])) {
$binary = $images[$imageFile];
@@ -350,6 +379,8 @@ class AssetController extends Controller
private function validateRequest(Request $request, ?string $assetId = null): array
{
$rfidRequired = $this->settings->get('asset.rfid_required', false);
$nfcRequired = $this->settings->get('asset.nfc_required', false);
return $request->validate([
'name' => ['required', 'string', 'max:255'],
'serial_number' => ['nullable', 'string', 'max:255'],
@@ -372,6 +403,21 @@ class AssetController extends Controller
'capex_opex' => ['nullable', 'in:capex,opex'],
'vendor_contract_id' => ['nullable', 'uuid', 'exists:vendor_contracts,id'],
'metadata' => ['nullable', 'array'],
'rfid_tag' => array_filter([
'string','max:255',
$rfidRequired ? 'required' : null,
"unique:assets,rfid_tag,$assetId",
]),
'nfc_tag' => array_filter([
'string','max:255',
$nfcRequired ? 'required' : null,
"unique:assets,nfc_tag,$assetId",
]),
'label_template' => ['nullable','string','max:100'],
'is_consumable' => ['nullable','boolean'],
'quantity' => ['nullable','integer','min:1'],
'available_quantity' => ['nullable','integer','min:0'],
'is_pool' => ['nullable','boolean'],
]);
}
}

View File

@@ -12,6 +12,7 @@ use App\Models\AssetAudit;
use App\Models\AssetChangelog;
use App\Models\AssetMaintenance;
use App\Services\Logging\ActivityLogger;
use App\Services\Integration\WebhookDispatcher;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
@@ -21,7 +22,8 @@ class AssetService
{
public function __construct(
private readonly SystemSettingService $settings,
private readonly ActivityLogger $logger
private readonly ActivityLogger $logger,
private readonly WebhookDispatcher $webhooks
)
{
}
@@ -50,6 +52,7 @@ class AssetService
}
$this->logHistory($asset, 'created', 'Aset dibuat', $data);
$this->logChangelog($asset, 'created', $data);
$this->dispatchWebhook('asset.created', $asset, $data);
return $asset;
});
@@ -67,6 +70,7 @@ class AssetService
}
$this->logHistory($asset, 'updated', 'Aset diperbarui', $data);
$this->logChangelog($asset, 'updated', $data);
$this->dispatchWebhook('asset.updated', $asset, $data);
return $asset;
});
@@ -100,11 +104,13 @@ class AssetService
private function generateQr(Asset $asset): void
{
$path = "qr/{$asset->code}.svg";
$format = $this->settings->get('asset.qr_format', 'svg');
$size = (int) $this->settings->get('asset.qr_size', 300);
$path = "qr/{$asset->code}.{$format}";
$url = route('assets.public.show', $asset);
$image = QrCode::format('svg')
->size(300)
$image = QrCode::format($format)
->size(max(100, $size))
->margin(1)
->generate($url);
@@ -161,6 +167,7 @@ class AssetService
}
$this->logHistory($asset, 'movement', 'Perpindahan aset', $movement->toArray());
$this->dispatchWebhook('asset.movement', $asset, $movement->toArray());
return $movement;
});
@@ -201,6 +208,7 @@ class AssetService
}
$this->logHistory($asset, 'disposal', 'Aset didispose', $disposal->toArray());
$this->dispatchWebhook('asset.disposal', $asset, $disposal->toArray());
return $disposal;
});
@@ -231,6 +239,7 @@ class AssetService
'disposal_id' => $disposal->id,
'notes' => $notes,
]);
$this->dispatchWebhook('asset.reverse_disposal', $asset, ['disposal_id' => $disposal->id, 'notes' => $notes]);
return $asset;
});
@@ -252,6 +261,7 @@ class AssetService
]);
$this->logHistory($asset, 'audit', 'Audit aset', $audit->toArray());
$this->dispatchWebhook('asset.audit', $asset, $audit->toArray());
return $audit;
});
@@ -284,8 +294,19 @@ class AssetService
}
$this->logHistory($asset, 'maintenance', 'Perawatan aset', $maintenance->toArray());
$this->dispatchWebhook('asset.maintenance', $asset, $maintenance->toArray());
return $maintenance;
});
}
private function dispatchWebhook(string $event, Asset $asset, array $payload = []): void
{
$this->webhooks->send($event, [
'asset_id' => $asset->id,
'asset_code' => $asset->code ?? null,
'asset_status_id' => $asset->asset_status_id,
'payload' => $payload,
]);
}
}