695 lines
25 KiB
PHP
695 lines
25 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Master;
|
|
|
|
use App\Enums\OwnerVerificationAction;
|
|
use App\Enums\OwnerVerificationStatus;
|
|
use App\Enums\Permission;
|
|
use App\Enums\RawMaterialUnit;
|
|
use App\Models\OwnerVerificationRequest;
|
|
use App\Models\RawMaterial;
|
|
use App\Models\RawMaterialPrice;
|
|
use App\Models\User;
|
|
use App\Services\Concerns\CachesQuery;
|
|
use App\Services\Concerns\RunsInTransaction;
|
|
use App\Services\Media\MediaService;
|
|
use App\Services\System\PushNotificationService;
|
|
use App\Support\Media\MediaPresenter;
|
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class RawMaterialService
|
|
{
|
|
use CachesQuery, RunsInTransaction;
|
|
|
|
private const MAX_VARIANT_IMAGES = 5;
|
|
|
|
public function __construct(
|
|
private readonly MediaService $mediaService,
|
|
private readonly PushNotificationService $pushNotificationService,
|
|
) {}
|
|
|
|
public function paginateForIndex(array $tableQuery, string $isActive, string $stockStatus = ''): LengthAwarePaginator
|
|
{
|
|
$query = RawMaterial::query()
|
|
->with([
|
|
'pendingOwnerVerificationRequest.submittedBy.profile',
|
|
'prices' => fn ($query) => $query
|
|
->orderBy('created_at')
|
|
->with('media'),
|
|
])
|
|
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
|
$search = $tableQuery['search'];
|
|
$query->where(function (Builder $query) use ($search): void {
|
|
$query->where('name', 'like', "%{$search}%")
|
|
->orWhereHas('prices', fn (Builder $query) => $query->where('variant', 'like', "%{$search}%"));
|
|
});
|
|
})
|
|
->when($isActive !== '', fn (Builder $query) => $query->where('is_active', $isActive === '1'))
|
|
->when($stockStatus === 'out_of_stock', function (Builder $query): void {
|
|
$query->whereHas('prices', fn (Builder $priceQuery) => $priceQuery->where('stock', '<=', 0));
|
|
})
|
|
->when($stockStatus === 'low_stock', function (Builder $query): void {
|
|
$minStock = [
|
|
RawMaterialUnit::YARD => 20,
|
|
RawMaterialUnit::METER => 10,
|
|
RawMaterialUnit::KILOGRAM => 5,
|
|
];
|
|
|
|
$query->whereHas('prices', function (Builder $priceQuery) use ($minStock): void {
|
|
$priceQuery->where('stock', '>', 0)->where(function (Builder $priceQuery) use ($minStock): void {
|
|
foreach (RawMaterialUnit::cases() as $unit) {
|
|
$priceQuery->orWhere(function (Builder $priceQuery) use ($unit, $minStock): void {
|
|
$priceQuery->whereHas('rawMaterial', fn (Builder $rawMaterialQuery) => $rawMaterialQuery->where('unit', $unit))
|
|
->where('stock', '<', $minStock[$unit]);
|
|
});
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
$this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']);
|
|
|
|
return $query
|
|
->paginate(25)
|
|
->withQueryString()
|
|
->through(function (RawMaterial $rawMaterial) {
|
|
$rawMaterial->prices->each(function (RawMaterialPrice $price) use ($rawMaterial): void {
|
|
$price->setAttribute('images', MediaPresenter::collection($price, 'images'));
|
|
$price->setAttribute('unit_abbreviation', $rawMaterial->unit->abbreviation());
|
|
$price->unsetRelation('rawMaterial');
|
|
});
|
|
|
|
$pendingRequest = $rawMaterial->pendingOwnerVerificationRequest;
|
|
|
|
$rawMaterial->setAttribute('has_pending_request', $pendingRequest !== null);
|
|
$rawMaterial->setAttribute('pending_request_id', $pendingRequest?->id);
|
|
$rawMaterial->setAttribute('pending_request_action', $pendingRequest?->action->value);
|
|
$rawMaterial->setAttribute('pending_request_action_label', $pendingRequest?->action->label());
|
|
$rawMaterial->setAttribute('pending_request_submitted_by_name', $pendingRequest?->submittedBy?->profile?->full_name ?? $pendingRequest?->submittedBy?->username);
|
|
$rawMaterial->setAttribute(
|
|
'display_is_active',
|
|
$pendingRequest?->pendingToggleIsActive() ?? $rawMaterial->is_active,
|
|
);
|
|
|
|
return $rawMaterial;
|
|
});
|
|
}
|
|
|
|
public function findForEdit(RawMaterial $rawMaterial): RawMaterial
|
|
{
|
|
$rawMaterial->load([
|
|
'prices' => fn ($query) => $query->orderBy('created_at')->with('media'),
|
|
]);
|
|
|
|
$rawMaterial->prices->each(function (RawMaterialPrice $price) use ($rawMaterial): void {
|
|
$price->setAttribute('images', MediaPresenter::collection($price, 'images'));
|
|
$price->setAttribute('unit_abbreviation', $rawMaterial->unit->abbreviation());
|
|
$price->unsetRelation('rawMaterial');
|
|
});
|
|
|
|
return $rawMaterial;
|
|
}
|
|
|
|
public function create(array $validated, User $user): void
|
|
{
|
|
$isOwner = $user->can(Permission::OWNER_VERIFICATIONS_VERIFY->value);
|
|
|
|
$this->runInTransaction(
|
|
function () use ($validated, $user, $isOwner): void {
|
|
$rawMaterial = RawMaterial::create([
|
|
'name' => $validated['name'],
|
|
'unit' => $validated['unit'],
|
|
'is_active' => $isOwner,
|
|
]);
|
|
|
|
foreach ($validated['prices'] as $index => $priceData) {
|
|
$this->createPrice($rawMaterial, $priceData, $index);
|
|
}
|
|
|
|
if (! $isOwner) {
|
|
OwnerVerificationRequest::create([
|
|
'action' => OwnerVerificationAction::CREATE,
|
|
'status' => OwnerVerificationStatus::PENDING,
|
|
'subject_type' => RawMaterial::class,
|
|
'subject_id' => $rawMaterial->id,
|
|
'submitted_by_id' => $user->id,
|
|
'payload' => [
|
|
'old' => null,
|
|
'new' => $this->snapshotRawMaterial($rawMaterial->fresh(['prices'])),
|
|
],
|
|
]);
|
|
}
|
|
},
|
|
'Gagal membuat bahan baku',
|
|
);
|
|
|
|
if ($isOwner) {
|
|
$this->cacheForgetByPattern('master:raw_materials:*');
|
|
}
|
|
|
|
if (! $isOwner) {
|
|
$this->notifyForPendingRequest(
|
|
$user,
|
|
'Tambah Bahan Baku',
|
|
"Pengajuan tambah bahan baku '{$validated['name']}' menunggu verifikasi owner.",
|
|
route('admin.master.raw_materials.index', ['search' => $validated['name']]),
|
|
$validated['name'],
|
|
);
|
|
}
|
|
}
|
|
|
|
public function update(RawMaterial $rawMaterial, array $validated, User $user): void
|
|
{
|
|
$isOwner = $user->can(Permission::OWNER_VERIFICATIONS_VERIFY->value);
|
|
|
|
$this->runInTransaction(
|
|
function () use ($validated, $rawMaterial, $user, $isOwner): void {
|
|
if ($isOwner) {
|
|
$payload = $this->enrichPayload($this->buildPayloadFromValidated($validated));
|
|
$this->applyPayloadToRawMaterial($rawMaterial, $payload);
|
|
|
|
foreach ($validated['prices'] as $index => $priceData) {
|
|
if (! empty($priceData['id'])) {
|
|
$price = $rawMaterial->prices()->find($priceData['id']);
|
|
if ($price) {
|
|
$this->syncPriceImages($price, $priceData, $index);
|
|
}
|
|
} else {
|
|
$this->createPrice($rawMaterial, $priceData, $index);
|
|
}
|
|
}
|
|
} else {
|
|
$verificationRequest = OwnerVerificationRequest::create([
|
|
'action' => OwnerVerificationAction::UPDATE,
|
|
'status' => OwnerVerificationStatus::PENDING,
|
|
'subject_type' => RawMaterial::class,
|
|
'subject_id' => $rawMaterial->id,
|
|
'submitted_by_id' => $user->id,
|
|
'payload' => [
|
|
'old' => $this->snapshotRawMaterial($rawMaterial),
|
|
'new' => $this->enrichPayload($this->buildPayloadFromValidated($validated)),
|
|
],
|
|
]);
|
|
|
|
foreach ($validated['prices'] as $index => $priceData) {
|
|
$isNewPrice = empty($priceData['id']);
|
|
$this->syncRequestPriceImages($verificationRequest, $priceData, $index, required: $isNewPrice);
|
|
}
|
|
}
|
|
},
|
|
'Gagal memperbarui bahan baku',
|
|
);
|
|
|
|
if ($isOwner) {
|
|
$this->cacheForgetByPattern('master:raw_materials:*');
|
|
}
|
|
|
|
if (! $isOwner) {
|
|
$this->notifyForPendingRequest(
|
|
$user,
|
|
'Ubah Bahan Baku',
|
|
"Pengajuan ubah bahan baku '{$rawMaterial->name}' menunggu verifikasi owner.",
|
|
route('admin.master.raw_materials.index', ['search' => $rawMaterial->name]),
|
|
$rawMaterial->name,
|
|
);
|
|
}
|
|
}
|
|
|
|
public function delete(RawMaterial $rawMaterial, User $user): void
|
|
{
|
|
$isOwner = $user->can(Permission::OWNER_VERIFICATIONS_VERIFY->value);
|
|
|
|
if ($isOwner) {
|
|
$this->applyDeleteSubject($rawMaterial);
|
|
$this->cacheForgetByPattern('master:raw_materials:*');
|
|
|
|
return;
|
|
}
|
|
|
|
$this->runInTransaction(
|
|
function () use ($rawMaterial, $user): void {
|
|
OwnerVerificationRequest::create([
|
|
'action' => OwnerVerificationAction::DELETE,
|
|
'status' => OwnerVerificationStatus::PENDING,
|
|
'subject_type' => RawMaterial::class,
|
|
'subject_id' => $rawMaterial->id,
|
|
'submitted_by_id' => $user->id,
|
|
'payload' => [
|
|
'old' => $this->snapshotRawMaterial($rawMaterial),
|
|
'new' => null,
|
|
],
|
|
]);
|
|
},
|
|
'Gagal mengajukan penghapusan bahan baku',
|
|
);
|
|
|
|
$this->notifyForPendingRequest(
|
|
$user,
|
|
'Hapus Bahan Baku',
|
|
"Pengajuan hapus bahan baku '{$rawMaterial->name}' menunggu verifikasi owner.",
|
|
route('admin.master.raw_materials.index', ['search' => $rawMaterial->name]),
|
|
$rawMaterial->name,
|
|
);
|
|
}
|
|
|
|
public function toggleStatus(RawMaterial $rawMaterial, array $validated, User $user): void
|
|
{
|
|
$isOwner = $user->can(Permission::OWNER_VERIFICATIONS_VERIFY->value);
|
|
|
|
if ($isOwner) {
|
|
$rawMaterial->update([
|
|
'is_active' => (bool) $validated['is_active'],
|
|
]);
|
|
|
|
$this->cacheForgetByPattern('master:raw_materials:*');
|
|
|
|
return;
|
|
}
|
|
|
|
$this->runInTransaction(
|
|
function () use ($rawMaterial, $validated, $user): void {
|
|
OwnerVerificationRequest::create([
|
|
'action' => OwnerVerificationAction::TOGGLE_STATUS,
|
|
'status' => OwnerVerificationStatus::PENDING,
|
|
'subject_type' => RawMaterial::class,
|
|
'subject_id' => $rawMaterial->id,
|
|
'submitted_by_id' => $user->id,
|
|
'payload' => [
|
|
'old' => [
|
|
'name' => $rawMaterial->name,
|
|
'is_active' => $rawMaterial->is_active,
|
|
],
|
|
'new' => [
|
|
'name' => $rawMaterial->name,
|
|
'is_active' => (bool) $validated['is_active'],
|
|
],
|
|
],
|
|
]);
|
|
},
|
|
'Gagal mengajukan perubahan status bahan baku',
|
|
);
|
|
|
|
$statusLabel = $validated['is_active'] ? 'aktif' : 'nonaktif';
|
|
|
|
$this->notifyForPendingRequest(
|
|
$user,
|
|
'Ubah Status Bahan Baku',
|
|
"Pengajuan ubah status bahan baku '{$rawMaterial->name}' menjadi {$statusLabel} menunggu verifikasi owner.",
|
|
route('admin.master.raw_materials.index', ['search' => $rawMaterial->name]),
|
|
$rawMaterial->name,
|
|
);
|
|
}
|
|
|
|
public function applyVerificationRequest(OwnerVerificationRequest $verificationRequest): void
|
|
{
|
|
match ($verificationRequest->action) {
|
|
OwnerVerificationAction::CREATE => $this->applyCreate($verificationRequest),
|
|
OwnerVerificationAction::UPDATE => $this->applyUpdate($verificationRequest),
|
|
OwnerVerificationAction::DELETE => $this->applyDelete($verificationRequest),
|
|
OwnerVerificationAction::TOGGLE_STATUS => $this->applyToggleStatus($verificationRequest),
|
|
default => throw ValidationException::withMessages([
|
|
'action' => 'Aksi verifikasi bahan baku tidak didukung.',
|
|
]),
|
|
};
|
|
|
|
$this->cacheForgetByPattern('master:raw_materials:*');
|
|
}
|
|
|
|
public function rejectVerificationRequest(OwnerVerificationRequest $verificationRequest): void
|
|
{
|
|
match ($verificationRequest->action) {
|
|
OwnerVerificationAction::CREATE => $this->rejectCreate($verificationRequest),
|
|
OwnerVerificationAction::UPDATE => $this->rollbackUpdate($verificationRequest),
|
|
OwnerVerificationAction::DELETE, OwnerVerificationAction::TOGGLE_STATUS => null,
|
|
default => throw ValidationException::withMessages([
|
|
'action' => 'Aksi verifikasi bahan baku tidak didukung.',
|
|
]),
|
|
};
|
|
|
|
$this->cacheForgetByPattern('master:raw_materials:*');
|
|
}
|
|
|
|
public function clearVerificationRequestMedia(OwnerVerificationRequest $verificationRequest): void
|
|
{
|
|
$verificationRequest->load('media');
|
|
|
|
$newPayload = $this->payloadNew($verificationRequest);
|
|
|
|
foreach ($newPayload['prices'] ?? [] as $index => $_price) {
|
|
$verificationRequest->clearMediaCollection($verificationRequest->priceImageCollection((int) $index));
|
|
}
|
|
}
|
|
|
|
private function notifyForPendingRequest(User $user, string $typeLabel, string $body, string $submitterUrl, ?string $search = null): void
|
|
{
|
|
$ownerUrl = route('admin.master.raw_materials.index');
|
|
if ($search !== null) {
|
|
$ownerUrl = route('admin.master.raw_materials.index', ['search' => $search]);
|
|
}
|
|
|
|
$this->pushNotificationService->sendToRoles(
|
|
"📦 {$typeLabel} Menunggu Persetujuan Owner",
|
|
$body,
|
|
['owner', 'developer', 'direktur'],
|
|
$ownerUrl,
|
|
);
|
|
|
|
$this->pushNotificationService->sendToUser(
|
|
'📤 Pengajuan Terkirim',
|
|
$body,
|
|
$user->id,
|
|
$submitterUrl,
|
|
);
|
|
}
|
|
|
|
public function applyCreate(OwnerVerificationRequest $verificationRequest): void
|
|
{
|
|
$rawMaterial = $verificationRequest->subject;
|
|
|
|
if (! $rawMaterial instanceof RawMaterial) {
|
|
throw ValidationException::withMessages([
|
|
'raw_material' => 'Bahan baku tidak ditemukan.',
|
|
]);
|
|
}
|
|
|
|
$rawMaterial->update([
|
|
'is_active' => true,
|
|
]);
|
|
}
|
|
|
|
public function applyUpdate(OwnerVerificationRequest $verificationRequest): void
|
|
{
|
|
$rawMaterial = $verificationRequest->subject;
|
|
|
|
if (! $rawMaterial instanceof RawMaterial) {
|
|
throw ValidationException::withMessages([
|
|
'raw_material' => 'Bahan baku tidak ditemukan.',
|
|
]);
|
|
}
|
|
|
|
$this->applyPayloadToRawMaterial($rawMaterial, $this->payloadNew($verificationRequest), $verificationRequest);
|
|
}
|
|
|
|
public function applyDelete(OwnerVerificationRequest $verificationRequest): void
|
|
{
|
|
$rawMaterial = $verificationRequest->subject;
|
|
|
|
if (! $rawMaterial instanceof RawMaterial) {
|
|
throw ValidationException::withMessages([
|
|
'raw_material' => 'Bahan baku tidak ditemukan.',
|
|
]);
|
|
}
|
|
|
|
$this->applyDeleteSubject($rawMaterial);
|
|
}
|
|
|
|
private function applyDeleteSubject(RawMaterial $rawMaterial): void
|
|
{
|
|
$this->runInTransaction(
|
|
function () use ($rawMaterial): void {
|
|
$rawMaterial->prices()->each(function (RawMaterialPrice $price): void {
|
|
$price->clearMediaCollection('images');
|
|
});
|
|
$rawMaterial->prices()->delete();
|
|
$rawMaterial->delete();
|
|
},
|
|
'Gagal menghapus bahan baku',
|
|
);
|
|
}
|
|
|
|
public function applyToggleStatus(OwnerVerificationRequest $verificationRequest): void
|
|
{
|
|
$rawMaterial = $verificationRequest->subject;
|
|
|
|
if (! $rawMaterial instanceof RawMaterial) {
|
|
throw ValidationException::withMessages([
|
|
'raw_material' => 'Bahan baku tidak ditemukan.',
|
|
]);
|
|
}
|
|
|
|
$newPayload = $this->payloadNew($verificationRequest);
|
|
|
|
$rawMaterial->update([
|
|
'is_active' => (bool) ($newPayload['is_active'] ?? false),
|
|
]);
|
|
}
|
|
|
|
private function applyPayloadToRawMaterial(
|
|
RawMaterial $rawMaterial,
|
|
array $payload,
|
|
?OwnerVerificationRequest $verificationRequest = null,
|
|
): void {
|
|
$rawMaterial->update([
|
|
'name' => $payload['name'],
|
|
'unit' => $payload['unit'] ?? $rawMaterial->unit,
|
|
]);
|
|
|
|
if (array_key_exists('is_active', $payload)) {
|
|
$rawMaterial->update([
|
|
'is_active' => (bool) $payload['is_active'],
|
|
]);
|
|
}
|
|
|
|
$submittedPriceIds = collect($payload['prices'] ?? [])
|
|
->pluck('id')
|
|
->filter()
|
|
->map(fn ($id) => (int) $id)
|
|
->all();
|
|
|
|
$rawMaterial->prices()
|
|
->whereNotIn('id', $submittedPriceIds)
|
|
->get()
|
|
->each(function (RawMaterialPrice $price): void {
|
|
$price->clearMediaCollection('images');
|
|
$price->delete();
|
|
});
|
|
|
|
foreach ($payload['prices'] ?? [] as $index => $priceData) {
|
|
if (! empty($priceData['id'])) {
|
|
$price = $rawMaterial->prices()->findOrFail($priceData['id']);
|
|
$price->update([
|
|
'variant' => $priceData['variant'],
|
|
'price' => $priceData['price'],
|
|
'stock' => $priceData['stock'],
|
|
]);
|
|
|
|
if ($verificationRequest !== null) {
|
|
$this->applyPriceImageChanges($verificationRequest, $price, $priceData, (int) $index);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
$price = $rawMaterial->prices()->create([
|
|
'variant' => $priceData['variant'],
|
|
'price' => $priceData['price'],
|
|
'stock' => $priceData['stock'],
|
|
]);
|
|
|
|
if ($verificationRequest !== null) {
|
|
$this->copyRequestPriceImages($verificationRequest, (int) $index, $price);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function rejectCreate(OwnerVerificationRequest $verificationRequest): void
|
|
{
|
|
$rawMaterial = $verificationRequest->subject;
|
|
|
|
if (! $rawMaterial instanceof RawMaterial) {
|
|
return;
|
|
}
|
|
|
|
$this->runInTransaction(
|
|
function () use ($rawMaterial): void {
|
|
$rawMaterial->prices()->each(function (RawMaterialPrice $price): void {
|
|
$price->clearMediaCollection('images');
|
|
});
|
|
$rawMaterial->prices()->delete();
|
|
$rawMaterial->delete();
|
|
},
|
|
'Gagal menolak bahan baku',
|
|
);
|
|
}
|
|
|
|
private function rollbackUpdate(OwnerVerificationRequest $verificationRequest): void
|
|
{
|
|
$rawMaterial = $verificationRequest->subject;
|
|
|
|
if (! $rawMaterial instanceof RawMaterial) {
|
|
throw ValidationException::withMessages([
|
|
'raw_material' => 'Bahan baku tidak ditemukan.',
|
|
]);
|
|
}
|
|
|
|
$oldPayload = $this->payloadOld($verificationRequest);
|
|
|
|
if ($oldPayload === []) {
|
|
return;
|
|
}
|
|
|
|
$this->applyPayloadToRawMaterial($rawMaterial, $oldPayload);
|
|
}
|
|
|
|
private function payloadOld(OwnerVerificationRequest $verificationRequest): array
|
|
{
|
|
$payload = $verificationRequest->payload ?? [];
|
|
|
|
if (array_key_exists('old', $payload)) {
|
|
return is_array($payload['old']) ? $payload['old'] : [];
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
private function payloadNew(OwnerVerificationRequest $verificationRequest): array
|
|
{
|
|
$payload = $verificationRequest->payload ?? [];
|
|
|
|
if (array_key_exists('new', $payload)) {
|
|
return is_array($payload['new']) ? $payload['new'] : [];
|
|
}
|
|
|
|
return $payload;
|
|
}
|
|
|
|
private function snapshotRawMaterial(RawMaterial $rawMaterial): array
|
|
{
|
|
$rawMaterial->load(['prices']);
|
|
|
|
return $this->enrichPayload([
|
|
'name' => $rawMaterial->name,
|
|
'unit' => $rawMaterial->unit->value,
|
|
'is_active' => $rawMaterial->is_active,
|
|
'prices' => $rawMaterial->prices
|
|
->map(fn (RawMaterialPrice $price) => [
|
|
'id' => $price->id,
|
|
'variant' => $price->variant,
|
|
'price' => $price->price,
|
|
'stock' => $price->stock,
|
|
])
|
|
->all(),
|
|
]);
|
|
}
|
|
|
|
private function enrichPayload(array $data): array
|
|
{
|
|
if (isset($data['unit'])) {
|
|
$unit = $data['unit'] instanceof RawMaterialUnit
|
|
? $data['unit']
|
|
: RawMaterialUnit::from($data['unit']);
|
|
|
|
$data['unit'] = $unit->value;
|
|
$data['unit_label'] = $unit->label();
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
private function buildPayloadFromValidated(array $validated): array
|
|
{
|
|
return [
|
|
'name' => $validated['name'],
|
|
'unit' => $validated['unit'],
|
|
'prices' => collect($validated['prices'] ?? [])
|
|
->map(fn (array $priceData) => [
|
|
'id' => $priceData['id'] ?? null,
|
|
'variant' => $priceData['variant'],
|
|
'price' => $priceData['price'],
|
|
'stock' => $priceData['stock'],
|
|
'remove_media_ids' => $priceData['remove_media_ids'] ?? [],
|
|
])
|
|
->all(),
|
|
];
|
|
}
|
|
|
|
private function applySorting(Builder $query, string $sort, string $direction): void
|
|
{
|
|
if (in_array($sort, ['name', 'unit', 'is_active'], true)) {
|
|
$query->orderBy($sort, $direction);
|
|
|
|
return;
|
|
}
|
|
|
|
$query->latest();
|
|
}
|
|
|
|
private function createPrice(RawMaterial $rawMaterial, array $priceData, int $index): RawMaterialPrice
|
|
{
|
|
$price = $rawMaterial->prices()->create([
|
|
'variant' => $priceData['variant'],
|
|
'price' => $priceData['price'],
|
|
'stock' => $priceData['stock'],
|
|
]);
|
|
|
|
$this->syncPriceImages($price, $priceData, $index);
|
|
|
|
return $price;
|
|
}
|
|
|
|
private function syncPriceImages(RawMaterialPrice $price, array $priceData, int $index): void
|
|
{
|
|
$this->mediaService->syncCollection(
|
|
$price,
|
|
'images',
|
|
$priceData['images'] ?? null,
|
|
$priceData['remove_media_ids'] ?? null,
|
|
self::MAX_VARIANT_IMAGES,
|
|
required: true,
|
|
errorKey: "prices.{$index}.images",
|
|
s3Keys: $priceData['s3_keys'] ?? null,
|
|
);
|
|
}
|
|
|
|
private function syncRequestPriceImages(
|
|
OwnerVerificationRequest $verificationRequest,
|
|
array $priceData,
|
|
int $index,
|
|
bool $required = true,
|
|
): void {
|
|
$collection = $verificationRequest->priceImageCollection($index);
|
|
|
|
$this->mediaService->syncCollection(
|
|
$verificationRequest,
|
|
$collection,
|
|
$priceData['images'] ?? null,
|
|
$priceData['remove_media_ids'] ?? null,
|
|
self::MAX_VARIANT_IMAGES,
|
|
required: $required,
|
|
errorKey: "prices.{$index}.images",
|
|
s3Keys: $priceData['s3_keys'] ?? null,
|
|
);
|
|
}
|
|
|
|
private function applyPriceImageChanges(
|
|
OwnerVerificationRequest $verificationRequest,
|
|
RawMaterialPrice $price,
|
|
array $priceData,
|
|
int $index,
|
|
): void {
|
|
$removeIds = $priceData['remove_media_ids'] ?? [];
|
|
|
|
if ($removeIds !== []) {
|
|
$price->getMedia('images')
|
|
->whereIn('id', $removeIds)
|
|
->each->delete();
|
|
}
|
|
|
|
$this->copyRequestPriceImages($verificationRequest, $index, $price);
|
|
|
|
if ($price->getMedia('images')->isEmpty()) {
|
|
throw ValidationException::withMessages([
|
|
"prices.{$index}.images" => 'Foto varian wajib diisi.',
|
|
]);
|
|
}
|
|
}
|
|
|
|
private function copyRequestPriceImages(OwnerVerificationRequest $verificationRequest, int $index, RawMaterialPrice $price): void
|
|
{
|
|
$verificationRequest->getMedia($verificationRequest->priceImageCollection($index))
|
|
->each(fn ($media) => $media->copy($price, 'images'));
|
|
}
|
|
}
|