dstpabuaran.com/app/Services/Admin/Master/RawMaterial/RawMaterialService.php

272 lines
10 KiB
PHP

<?php
namespace App\Services\Admin\Master\RawMaterial;
use App\Models\RawMaterial;
use App\Models\RawMaterialPrice;
use App\Services\Concerns\RegistersMedia;
use App\Services\S3PresignedService;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;
class RawMaterialService
{
use RegistersMedia;
public function __construct(
private S3PresignedService $s3Service,
) {}
public function getAll(array $filters = []): Collection
{
return RawMaterial::select(['id', 'name', 'unit', 'is_active'])
->with([
'rawMaterialPrices:id,raw_material_id,variant,price,stock',
'rawMaterialPrices.media',
])
->when($filters['is_active'] ?? null, fn ($q, $isActive) => $q->where('is_active', $isActive === 'true'))
->when($filters['name'] ?? null, fn ($q, $name) => $q->where('name', 'like', "%{$name}%"))
->latest()
->get()
->each(function ($rawMaterial) {
$rawMaterial->rawMaterialPrices->each(function ($price) {
$media = $price->getMedia('photos');
$price->photo_url = $media->first()
? $this->s3Service->getTemporaryUrl($media->first()->file_name)
: null;
});
});
}
public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc', array $filters = []): LengthAwarePaginator
{
$paginator = RawMaterial::query()
->select(['id', 'name', 'unit', 'is_active'])
->with([
'rawMaterialPrices:id,raw_material_id,variant,price,stock',
])
->when($search, fn ($q) => $q->where('name', 'like', "%{$search}%"))
->when(($filters['is_active'] ?? null) !== null && ($filters['is_active'] ?? null) !== '', function ($q) use ($filters) {
$q->where('is_active', $filters['is_active'] === 'true');
})
->when(($filters['stock'] ?? null) === 'empty', function ($q) {
$q->whereRaw('(SELECT IFNULL(SUM(stock), 0) FROM raw_material_prices WHERE raw_material_prices.raw_material_id = raw_materials.id AND raw_material_prices.deleted_at IS NULL) = 0');
})
->when(($filters['stock'] ?? null) === 'low', function ($q) {
$q->whereRaw('(SELECT IFNULL(SUM(stock), 0) FROM raw_material_prices WHERE raw_material_prices.raw_material_id = raw_materials.id AND raw_material_prices.deleted_at IS NULL) BETWEEN 0.0001 AND 9.9999');
})
->orderBy($sort, $direction)
->paginate($perPage);
$paginator->getCollection()->each(function ($rawMaterial) {
$rawMaterial->rawMaterialPrices->each(function ($price) {
$media = $price->getMedia('photos');
$price->photo_url = $media->first()
? $this->s3Service->getTemporaryUrl($media->first()->file_name)
: null;
});
});
return $paginator;
}
public function store(array $data): RawMaterial
{
return DB::transaction(function () use ($data) {
$rawMaterial = RawMaterial::create([
'name' => $data['name'],
'unit' => $data['unit'],
'is_active' => $data['is_active'] ?? true,
]);
$now = now();
$priceRows = collect($data['variants'])->map(fn ($v) => [
'raw_material_id' => $rawMaterial->id,
'variant' => $v['variant'],
'price' => $v['price'],
'stock' => $v['stock'],
'created_at' => $now,
'updated_at' => $now,
])->toArray();
DB::table('raw_material_prices')->insert($priceRows);
$insertedPrices = RawMaterialPrice::where('raw_material_id', $rawMaterial->id)->get();
$variantMap = $insertedPrices->mapWithKeys(fn ($p) => [$p->variant => $p->id]);
foreach ($data['variants'] as $variantData) {
if (! empty($variantData['photo_key'])) {
$priceId = $variantMap[$variantData['variant']];
$priceModel = RawMaterialPrice::find($priceId);
$this->registerPhoto($priceModel, $variantData['photo_key']);
}
}
return $rawMaterial;
});
}
public function getForEdit(RawMaterial $rawMaterial): array
{
$rawMaterial->load([
'rawMaterialPrices.media',
]);
$variants = $rawMaterial->rawMaterialPrices->map(function (RawMaterialPrice $price) {
$media = $price->getMedia('photos');
$photoKey = $media->first()?->file_name;
$photoUrl = $media->first()
? $this->s3Service->getTemporaryUrl($media->first()->file_name)
: null;
return [
'id' => $price->id,
'variant' => $price->variant,
'price' => $price->price,
'stock' => $price->stock,
'photo_key' => $photoKey,
'photo_url' => $photoUrl,
];
});
return [
'id' => $rawMaterial->id,
'name' => $rawMaterial->name,
'unit' => $rawMaterial->unit->value,
'is_active' => $rawMaterial->is_active,
'raw_material_prices' => $variants,
];
}
public function update(RawMaterial $rawMaterial, array $data): RawMaterial
{
return DB::transaction(function () use ($rawMaterial, $data) {
$rawMaterial->update([
'name' => $data['name'],
'unit' => $data['unit'],
'is_active' => $data['is_active'] ?? $rawMaterial->is_active,
]);
$existingVariantIds = collect($data['variants'])
->pluck('id')
->filter()
->toArray();
$rawMaterial->rawMaterialPrices()
->whereNotIn('id', $existingVariantIds)
->each(function (RawMaterialPrice $price) {
$price->clearMediaCollection('photos');
$price->delete();
});
$existingPricesMap = RawMaterialPrice::whereIn('id', $existingVariantIds)
->with('media')
->get()
->mapWithKeys(fn ($p) => [$p->id => $p]);
$now = now();
$newVariantsData = collect($data['variants'])->filter(fn ($v) => ! isset($v['id']));
$newVariantIdMap = [];
if ($newVariantsData->isNotEmpty()) {
$newRows = $newVariantsData->map(fn ($v) => [
'raw_material_id' => $rawMaterial->id,
'variant' => $v['variant'],
'price' => $v['price'],
'stock' => $v['stock'],
'created_at' => $now,
'updated_at' => $now,
])->toArray();
DB::table('raw_material_prices')->insert($newRows);
$newlyCreated = RawMaterialPrice::where('raw_material_id', $rawMaterial->id)
->whereIn('variant', $newVariantsData->pluck('variant')->toArray())
->get();
$newVariantIdMap = $newlyCreated->mapWithKeys(fn ($p) => [$p->variant => $p->id])->toArray();
}
foreach ($data['variants'] as $variantData) {
$priceId = $variantData['id'] ?? $newVariantIdMap[$variantData['variant']] ?? null;
if (! $priceId) {
continue;
}
$priceModel = $existingPricesMap[$priceId] ?? RawMaterialPrice::find($priceId);
if (! $priceModel) {
continue;
}
$priceModel->update([
'variant' => $variantData['variant'],
'price' => $variantData['price'],
'stock' => $variantData['stock'],
]);
if (isset($variantData['photo_key'])) {
$existingKey = $priceModel->getMedia('photos')->first()?->file_name;
if ($existingKey !== $variantData['photo_key']) {
$priceModel->clearMediaCollection('photos');
if ($variantData['photo_key']) {
$this->registerPhoto($priceModel, $variantData['photo_key']);
}
}
}
}
return $rawMaterial;
});
}
public function destroy(RawMaterial $rawMaterial): bool
{
return DB::transaction(function () use ($rawMaterial) {
$rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) {
$price->clearMediaCollection('photos');
$price->delete();
});
return $rawMaterial->delete();
});
}
public function toggleStatus(RawMaterial $rawMaterial): void
{
$rawMaterial->update([
'is_active' => ! $rawMaterial->is_active,
]);
}
public function getVariantsForCutting(): array
{
return RawMaterial::query()
->select(['id', 'name', 'unit', 'is_active'])
->with([
'rawMaterialPrices:id,raw_material_id,variant,price,stock',
])
->active()
->orderBy('name')
->get()
->each(function (RawMaterial $rawMaterial) {
$rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) {
$media = $price->getFirstMedia('photos');
$price->photo_url = $media
? $this->s3Service->getTemporaryUrl($media->file_name)
: null;
});
});
}
private function registerPhoto(RawMaterialPrice $price, string $s3Key): void
{
$this->registerMedia(
model: $price,
s3Key: $s3Key,
collectionName: 'photos',
orderColumn: 1,
);
}
}