Apply new settings to QR, validation, and import flow
This commit is contained in:
@@ -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'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user