diff --git a/app/Http/Controllers/Admin/Manage/PurchaseController.php b/app/Http/Controllers/Admin/Manage/PurchaseController.php index 1f6842a..07da65c 100644 --- a/app/Http/Controllers/Admin/Manage/PurchaseController.php +++ b/app/Http/Controllers/Admin/Manage/PurchaseController.php @@ -56,6 +56,8 @@ public function store(PurchaseRequest $request): RedirectResponse 'unit_price' => $item['unit_price'], 'total_price' => $item['quantity'] * $item['unit_price'], ]); + + Product::find($item['product_id'])->increment('stock', $item['quantity']); } PurchaseItem::where('user_id', auth()->id()) @@ -89,6 +91,10 @@ public function update(PurchaseRequest $request, Purchase $purchase): RedirectRe 'total' => $total, ]); + foreach ($purchase->items as $item) { + $item->product->decrement('stock', $item->quantity); + } + $purchase->items()->delete(); foreach ($validated['items'] as $item) { @@ -99,6 +105,8 @@ public function update(PurchaseRequest $request, Purchase $purchase): RedirectRe 'unit_price' => $item['unit_price'], 'total_price' => $item['quantity'] * $item['unit_price'], ]); + + Product::find($item['product_id'])->increment('stock', $item['quantity']); } }); @@ -107,7 +115,12 @@ public function update(PurchaseRequest $request, Purchase $purchase): RedirectRe public function destroy(Purchase $purchase): RedirectResponse { - $purchase->delete(); + DB::transaction(function () use ($purchase) { + foreach ($purchase->items as $item) { + $item->product->decrement('stock', $item->quantity); + } + $purchase->delete(); + }); return redirect()->back()->with('success', 'Data berhasil dihapus'); } @@ -116,7 +129,15 @@ public function bulkDestroy(Request $request): RedirectResponse { $ids = $request->input('ids'); - Purchase::whereIn('id', $ids)->delete(); + DB::transaction(function () use ($ids) { + $purchases = Purchase::with('items')->whereIn('id', $ids)->get(); + foreach ($purchases as $purchase) { + foreach ($purchase->items as $item) { + $item->product->decrement('stock', $item->quantity); + } + $purchase->delete(); + } + }); return redirect()->back()->with('success', 'Data terpilih berhasil dihapus'); } diff --git a/app/Http/Controllers/Admin/Master/ProductController.php b/app/Http/Controllers/Admin/Master/ProductController.php index f077253..317b0f5 100644 --- a/app/Http/Controllers/Admin/Master/ProductController.php +++ b/app/Http/Controllers/Admin/Master/ProductController.php @@ -87,6 +87,7 @@ public function update(ProductRequest $request, Product $product): RedirectRespo $product->update([ 'name' => $validated['name'], 'description' => $validated['description'], + 'stock' => $validated['stock'] ?? 0, ]); $product->categories()->sync($validated['category_ids'] ?? []); diff --git a/app/Http/Requests/Admin/Master/ProductRequest.php b/app/Http/Requests/Admin/Master/ProductRequest.php index 04489c6..3988512 100644 --- a/app/Http/Requests/Admin/Master/ProductRequest.php +++ b/app/Http/Requests/Admin/Master/ProductRequest.php @@ -40,6 +40,7 @@ public function rules(): array 'images.*' => ['image', 'max:5120'], 'delete_image_ids' => ['nullable', 'array'], 'delete_image_ids.*' => ['integer'], + 'stock' => ['required', 'integer', 'min:0'], ]; } } diff --git a/app/Models/Product.php b/app/Models/Product.php index 2697316..d18f3a5 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -27,6 +27,7 @@ protected function casts(): array { return [ 'is_active' => 'boolean', + 'stock' => 'integer', ]; } diff --git a/database/migrations/2026_04_16_082802_create_products_table.php b/database/migrations/2026_04_16_082802_create_products_table.php index b93fd9e..5c1b7b0 100644 --- a/database/migrations/2026_04_16_082802_create_products_table.php +++ b/database/migrations/2026_04_16_082802_create_products_table.php @@ -16,6 +16,7 @@ public function up(): void $table->string('name', 100); $table->string('slug', 100); $table->text('description')->nullable(); + $table->integer('stock')->default(0); $table->boolean('is_active')->default(true); $table->timestamp('created_at')->useCurrent(); $table->timestamp('updated_at')->nullable()->useCurrentOnUpdate(); diff --git a/database/seeders/PurchaseSeeder.php b/database/seeders/PurchaseSeeder.php index f3ee40b..4e106f1 100644 --- a/database/seeders/PurchaseSeeder.php +++ b/database/seeders/PurchaseSeeder.php @@ -41,6 +41,8 @@ public function run(): void 'total_price' => $totalPrice, ]); + $product->increment('stock', $quantity); + $total += $totalPrice; } diff --git a/resources/js/pages/admin/master/product/edit.tsx b/resources/js/pages/admin/master/product/edit.tsx index c2bac7f..f603751 100644 --- a/resources/js/pages/admin/master/product/edit.tsx +++ b/resources/js/pages/admin/master/product/edit.tsx @@ -67,6 +67,7 @@ export default function ProductEdit({ product, categories }: { product: Product, thumbnail: File | null; images: File[]; delete_image_ids: number[]; + stock: string; _method: string; }>({ name: product.name || '', @@ -82,6 +83,7 @@ export default function ProductEdit({ product, categories }: { product: Product, thumbnail: null, images: [], delete_image_ids: [], + stock: product.stock?.toString() || '0', _method: 'PATCH', }); @@ -179,19 +181,36 @@ export default function ProductEdit({ product, categories }: { product: Product, Informasi Produk - - - setData('name', e.target.value)} - autoComplete='off' - placeholder='Contoh: Gamis Wanita' - maxLength={100} - /> - {errors.name &&

{errors.name}

} -
+
+ + + setData('name', e.target.value)} + autoComplete='off' + placeholder='Contoh: Gamis Wanita' + maxLength={100} + /> + {errors.name &&

{errors.name}

} +
+ + + + setData('stock', values.value)} + placeholder="0" + autoComplete='off' + /> + {errors.stock &&

{errors.stock}

} +
+
diff --git a/resources/js/pages/admin/master/product/partials/columns.tsx b/resources/js/pages/admin/master/product/partials/columns.tsx index eeb8586..e40125b 100644 --- a/resources/js/pages/admin/master/product/partials/columns.tsx +++ b/resources/js/pages/admin/master/product/partials/columns.tsx @@ -105,6 +105,24 @@ export const getColumns = ({ onDelete, onToggleStatus, onPreviewImage }: ColumnP }, meta: { title: "Harga" }, }, + { + accessorKey: "stock", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const product = row.original; + + return ( +
+ + {product.stock} + +
+ ); + }, + meta: { title: "Stok" }, + }, { accessorKey: "is_active", header: "Status", diff --git a/resources/js/types/product.ts b/resources/js/types/product.ts index 5d17eaa..ec7a780 100644 --- a/resources/js/types/product.ts +++ b/resources/js/types/product.ts @@ -25,6 +25,7 @@ export interface Product { created_at: string; updated_at: string; deleted_at: string | null; + stock: number; prices?: ProductPrice[]; categories?: Category[]; thumbnail_url?: string | null;