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;