From 3e715518fb545947031ff3808e75d070ec538728 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Mon, 17 Aug 2026 23:01:00 +0700 Subject: [PATCH] feat: implement product logging for status changes and updates in ProductService and ProductVariantService --- .../Admin/Master/Product/ProductService.php | 20 +++++++ .../Master/Product/ProductVariantService.php | 59 ++++++++++++++++++- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/app/Services/Admin/Master/Product/ProductService.php b/app/Services/Admin/Master/Product/ProductService.php index 33bf3da..e37809a 100644 --- a/app/Services/Admin/Master/Product/ProductService.php +++ b/app/Services/Admin/Master/Product/ProductService.php @@ -468,6 +468,8 @@ public function toggleStatus(Product $product): void { $this->assertNotPending($product); + $oldValues = $this->getProductLogValues($product); + $product->update([ 'status' => $product->status->value === 'active' ? 'inactive' : 'active', ]); @@ -480,12 +482,16 @@ public function toggleStatus(Product $product): void body: "Produk \"{$product->name}\" berhasil {$status}".' oleh '.auth()->user()->full_name.'.', url: route('admin.master.products.index', ['highlight' => $product->id]), ); + + $this->logUpdated($product, 'Produk', $oldValues, $this->getProductLogValues($product)); } public function toggleFeatured(Product $product): void { $this->assertNotPending($product); + $oldValues = $this->getProductLogValues($product); + $product->update([ 'is_featured' => ! $product->is_featured, ]); @@ -498,10 +504,14 @@ public function toggleFeatured(Product $product): void body: "Produk \"{$product->name}\" berhasil {$status}".' oleh '.auth()->user()->full_name.'.', url: route('admin.master.products.index', ['highlight' => $product->id]), ); + + $this->logUpdated($product, 'Produk', $oldValues, $this->getProductLogValues($product)); } public function approve(Product $product): void { + $oldValues = $this->getProductLogValues($product); + $product->update([ 'status' => ProductStatus::ACTIVE, ]); @@ -513,10 +523,14 @@ public function approve(Product $product): void url: route('admin.master.products.index', ['highlight' => $product->id]), additionalUser: $product->createdBy ?? null, ); + + $this->logUpdated($product, 'Produk', $oldValues, $this->getProductLogValues($product)); } public function reject(Product $product, string $reason = ''): void { + $oldValues = $this->getProductLogValues($product); + $product->update([ 'status' => ProductStatus::REJECTED, 'rejection_reason' => $reason, @@ -529,10 +543,14 @@ public function reject(Product $product, string $reason = ''): void url: route('admin.master.products.index', ['highlight' => $product->id]), additionalUser: $product->createdBy ?? null, ); + + $this->logUpdated($product, 'Produk', $oldValues, $this->getProductLogValues($product)); } public function resubmit(Product $product): void { + $oldValues = $this->getProductLogValues($product); + $product->update([ 'status' => ProductStatus::PENDING, 'rejection_reason' => null, @@ -544,6 +562,8 @@ public function resubmit(Product $product): void body: "Produk \"{$product->name}\" telah diajukan ulang oleh ".auth()->user()->full_name.'.', url: route('admin.master.products.index', ['highlight' => $product->id]), ); + + $this->logUpdated($product, 'Produk', $oldValues, $this->getProductLogValues($product)); } private function assertNotPending(Product $product): void diff --git a/app/Services/Admin/Master/Product/ProductVariantService.php b/app/Services/Admin/Master/Product/ProductVariantService.php index d073da4..6e2a2b8 100644 --- a/app/Services/Admin/Master/Product/ProductVariantService.php +++ b/app/Services/Admin/Master/Product/ProductVariantService.php @@ -8,6 +8,7 @@ use App\Enums\Role; use App\Models\Product; use App\Models\ProductVariant; +use App\Services\Concerns\LogsFormHistory; use App\Services\Concerns\RegistersMedia; use App\Services\NotificationService; use App\Services\S3PresignedService; @@ -18,7 +19,7 @@ class ProductVariantService { - use HasRoleChecks, RegistersMedia; + use HasRoleChecks, LogsFormHistory, RegistersMedia; public function __construct( private S3PresignedService $s3Service, @@ -171,6 +172,8 @@ public function update(ProductVariant $variant, array $data): ProductVariant { $this->assertNotPending($variant->product); + $oldValues = $this->getProductLogValues($variant->product); + DB::transaction(function () use ($variant, $data) { $oldData = $variant->only(['stock', 'reject_stock', 'retail_stock']); @@ -210,6 +213,9 @@ public function update(ProductVariant $variant, array $data): ProductVariant url: route('admin.master.products.index', ['highlight' => $variant->product_id]), ); + $product = $variant->product()->with(['categories:id,name', 'productVariants.productPrices'])->first(); + $this->logUpdated($product, 'Produk', $oldValues, $this->getProductLogValues($product)); + return $variant->fresh(); } @@ -217,6 +223,8 @@ public function destroy(Product $product, ProductVariant $variant): bool { $this->assertNotPending($product); + $oldValues = $this->getProductLogValues($product); + $result = DB::transaction(function () use ($variant) { return $variant->delete(); }); @@ -228,6 +236,9 @@ public function destroy(Product $product, ProductVariant $variant): bool url: route('admin.master.products.index'), ); + $product->refresh()->load(['categories:id,name', 'productVariants.productPrices']); + $this->logDeleted($product, 'Produk', $oldValues); + return $result; } @@ -237,6 +248,8 @@ public function transferStock(ProductVariant $variant, array $data): ProductVari $this->assertNotPending($variant->product); + $oldValues = $this->getProductLogValues($variant->product); + if ($variant->stock < $quantity) { throw ValidationException::withMessages([ 'quantity' => "Stok bagus tidak mencukupi. Stok tersedia: {$variant->stock}.", @@ -270,6 +283,9 @@ public function transferStock(ProductVariant $variant, array $data): ProductVari url: route('admin.master.products.index', ['highlight' => $variant->product_id]), ); + $product = $variant->product()->with(['categories:id,name', 'productVariants.productPrices'])->first(); + $this->logUpdated($product, 'Produk', $oldValues, $this->getProductLogValues($product)); + return $variant->fresh(); } @@ -281,4 +297,45 @@ private function assertNotPending(Product $product): void ]); } } + + private function getProductLogValues(Product $product): array + { + $product->load(['categories:id,name', 'productVariants.productPrices']); + + $priceTypeLabels = [ + 'distributor' => 'Harga Distributor', + 'agent' => 'Harga Agen', + 'sub_agent' => 'Harga Sub Agen', + 'wholesale' => 'Harga Grosir', + 'retail' => 'Harga Ecer', + 'tiktok' => 'Harga TikTok', + 'shopee' => 'Harga Shopee', + 'capital' => 'Harga Modal', + 'reject_capital' => 'Harga Reject Modal', + 'reject_selling' => 'Harga Reject Jual', + ]; + + return [ + 'Nama Produk' => $product->name, + 'Status' => $product->status?->label(), + 'Unggulan' => $this->formatBoolean($product->is_featured), + 'Kategori' => $product->categories->pluck('name')->toArray(), + 'Deskripsi' => $product->description, + 'Varian' => $product->productVariants->map(function ($variant) use ($priceTypeLabels) { + $variantData = [ + 'Nama Varian' => $variant->name, + 'Stok Bagus' => $variant->stock, + 'Stok Reject' => $variant->reject_stock, + 'Stok Retail' => $variant->retail_stock, + ]; + + foreach ($variant->productPrices as $price) { + $label = $priceTypeLabels[$price->type->value] ?? $price->type->label(); + $variantData[$label] = $this->formatCurrency($price->price); + } + + return $variantData; + })->toArray(), + ]; + } }