diff --git a/app/Services/Admin/Master/Product/ProductService.php b/app/Services/Admin/Master/Product/ProductService.php index 5e16e69..6ee2520 100644 --- a/app/Services/Admin/Master/Product/ProductService.php +++ b/app/Services/Admin/Master/Product/ProductService.php @@ -11,6 +11,7 @@ use App\Services\S3PresignedService; use App\Services\StockMutationService; use Illuminate\Contracts\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Validation\ValidationException; @@ -24,6 +25,12 @@ public function __construct( private StockMutationService $stockMutationService, ) {} + public function getNames(): Collection + { + return Product::select(['id', 'name']) + ->get(); + } + public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc', array $filters = []): LengthAwarePaginator { $paginator = Product::query() @@ -33,10 +40,10 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = 'productVariants:id,product_id,name,stock,reject_stock,retail_stock', 'productVariants.productPrices:id,variant_id,type,price', ]) - ->when($search, fn ($q) => $q->where('name', 'like', "%{$search}%")) - ->when($filters['name'] ?? null, fn ($q, $name) => $q->where('name', 'like', "%{$name}%")) - ->when($filters['status'] ?? null, fn ($q, $status) => $q->where('status', $status)) - ->when($filters['category'] ?? null, fn ($q, $categoryId) => $q->whereHas('categories', function ($cq) use ($categoryId) { + ->when($search, fn($q) => $q->where('name', 'like', "%{$search}%")) + ->when($filters['name'] ?? null, fn($q, $name) => $q->where('name', 'like', "%{$name}%")) + ->when($filters['status'] ?? null, fn($q, $status) => $q->where('status', $status)) + ->when($filters['category'] ?? null, fn($q, $categoryId) => $q->whereHas('categories', function ($cq) use ($categoryId) { $cq->where('categories.id', $categoryId); })) ->when(($filters['stock'] ?? null) === 'empty', function ($q) { @@ -51,7 +58,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = $paginator->getCollection()->each(function ($product) { $product->productVariants->each(function ($variant) { $media = $variant->getMedia('photos'); - $variant->photo_urls = $media->map(fn ($m) => $this->s3Service->getTemporaryUrl($m->file_name))->toArray(); + $variant->photo_urls = $media->map(fn($m) => $this->s3Service->getTemporaryUrl($m->file_name))->toArray(); }); }); @@ -77,7 +84,7 @@ public function store(array $data): Product // Bulk insert variants $now = now(); - $variantRows = collect($data['variants'])->map(fn ($v) => [ + $variantRows = collect($data['variants'])->map(fn($v) => [ 'product_id' => $product->id, 'name' => $v['name'], 'stock' => $v['stock'], @@ -91,7 +98,7 @@ public function store(array $data): Product // Map variant name -> variant ID $insertedVariants = ProductVariant::where('product_id', $product->id)->get(); - $variantMap = $insertedVariants->mapWithKeys(fn ($v) => [$v->name => $v->id]); + $variantMap = $insertedVariants->mapWithKeys(fn($v) => [$v->name => $v->id]); // Bulk insert prices $priceRows = []; @@ -137,7 +144,7 @@ public function store(array $data): Product NotificationService::notify( roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO], title: 'Produk Baru', - body: "Produk \"{$product->name}\" berhasil ditambahkan".' oleh '.auth()->user()->full_name.'.', + body: "Produk \"{$product->name}\" berhasil ditambahkan" . ' oleh ' . auth()->user()->full_name . '.', url: route('admin.master.products.index'), ); @@ -155,7 +162,7 @@ public function getForEdit(Product $product): array $variants = $product->productVariants->map(function (ProductVariant $variant) { $media = $variant->getMedia('photos'); $photoKeys = $media->pluck('file_name')->toArray(); - $photoUrls = $media->map(fn ($m) => $this->s3Service->getTemporaryUrl($m->file_name))->toArray(); + $photoUrls = $media->map(fn($m) => $this->s3Service->getTemporaryUrl($m->file_name))->toArray(); return [ 'id' => $variant->id, @@ -165,7 +172,7 @@ public function getForEdit(Product $product): array 'retail_stock' => $variant->retail_stock, 'photo_keys' => $photoKeys, 'photo_urls' => $photoUrls, - 'prices' => $variant->productPrices->map(fn ($p) => [ + 'prices' => $variant->productPrices->map(fn($p) => [ 'type' => $p->type->value, 'price' => $p->price, ]), @@ -223,7 +230,7 @@ public function update(Product $product, array $data): Product $existingVariantsMap = ProductVariant::whereIn('id', $existingVariantIds) ->with('media') ->get() - ->mapWithKeys(fn ($v) => [$v->id => $v]); + ->mapWithKeys(fn($v) => [$v->id => $v]); // Collect old stock data + identify changed variants $oldStockDataMap = []; @@ -249,7 +256,7 @@ public function update(Product $product, array $data): Product // Bulk update changed variants (only changed ones, not all) if ($changedIds !== []) { $changedUpdates = collect($data['variants']) - ->filter(fn ($v) => isset($v['id']) && in_array($v['id'], $changedIds)); + ->filter(fn($v) => isset($v['id']) && in_array($v['id'], $changedIds)); foreach ($changedUpdates as $variantData) { $existingVariantsMap[$variantData['id']]->update([ @@ -262,11 +269,11 @@ public function update(Product $product, array $data): Product } // Bulk create new variants - $newVariantsData = collect($data['variants'])->filter(fn ($v) => ! isset($v['id'])); + $newVariantsData = collect($data['variants'])->filter(fn($v) => ! isset($v['id'])); $newVariantIdMap = []; if ($newVariantsData->isNotEmpty()) { - $newVariantRows = $newVariantsData->map(fn ($v) => [ + $newVariantRows = $newVariantsData->map(fn($v) => [ 'product_id' => $product->id, 'name' => $v['name'], 'stock' => $v['stock'], @@ -283,7 +290,7 @@ public function update(Product $product, array $data): Product ->whereIn('name', $newVariantsData->pluck('name')->toArray()) ->get(); - $newVariantIdMap = $newlyCreated->mapWithKeys(fn ($v) => [$v->name => $v->id])->toArray(); + $newVariantIdMap = $newlyCreated->mapWithKeys(fn($v) => [$v->name => $v->id])->toArray(); } // Build variant_id lookup: existing by id, new by name @@ -347,7 +354,7 @@ public function update(Product $product, array $data): Product } } - $changedModels = collect($changedIds)->map(fn ($id) => $existingVariantsMap[$id])->filter(); + $changedModels = collect($changedIds)->map(fn($id) => $existingVariantsMap[$id])->filter(); $this->stockMutationService->recordBulkAdjustment( $changedModels, $oldStockDataMap, @@ -388,7 +395,7 @@ public function update(Product $product, array $data): Product NotificationService::notify( roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO], title: 'Produk Diperbarui', - body: "Produk \"{$product->name}\" berhasil diperbarui".' oleh '.auth()->user()->full_name.'.', + body: "Produk \"{$product->name}\" berhasil diperbarui" . ' oleh ' . auth()->user()->full_name . '.', url: route('admin.master.products.index'), ); @@ -414,7 +421,7 @@ public function destroy(Product $product): bool NotificationService::notify( roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO], title: 'Produk Dihapus', - body: "Produk \"{$product->name}\" berhasil dihapus".' oleh '.auth()->user()->full_name.'.', + body: "Produk \"{$product->name}\" berhasil dihapus" . ' oleh ' . auth()->user()->full_name . '.', url: route('admin.master.products.index'), ); @@ -439,7 +446,7 @@ public function approve(Product $product): void NotificationService::notify( roles: [Role::OWNER, Role::DEVELOPER], title: 'Produk Disetujui', - body: "Produk \"{$product->name}\" telah disetujui oleh ".auth()->user()->full_name.'.', + body: "Produk \"{$product->name}\" telah disetujui oleh " . auth()->user()->full_name . '.', url: route('admin.master.products.index'), additionalUser: $product->createdBy ?? null, ); @@ -455,7 +462,7 @@ public function reject(Product $product, string $reason = ''): void NotificationService::notify( roles: [Role::OWNER, Role::DEVELOPER], title: 'Produk Ditolak', - body: "Produk \"{$product->name}\" telah ditolak oleh ".auth()->user()->full_name.'.', + body: "Produk \"{$product->name}\" telah ditolak oleh " . auth()->user()->full_name . '.', url: route('admin.master.products.index'), additionalUser: $product->createdBy ?? null, ); @@ -471,7 +478,7 @@ public function resubmit(Product $product): void NotificationService::notify( roles: [Role::OWNER, Role::DEVELOPER], title: 'Produk Diajukan Ulang', - body: "Produk \"{$product->name}\" telah diajukan ulang oleh ".auth()->user()->full_name.'.', + body: "Produk \"{$product->name}\" telah diajukan ulang oleh " . auth()->user()->full_name . '.', url: route('admin.master.products.index'), ); }