feat: add validation for product prices in transaction creation and editing
This commit is contained in:
parent
806c42dfd2
commit
7164bd96d3
@ -31,7 +31,7 @@ public function rules(): array
|
|||||||
'retail_stock' => ['required', 'integer', 'min:0'],
|
'retail_stock' => ['required', 'integer', 'min:0'],
|
||||||
'photo_keys' => ['required', 'array', 'min:1', 'max:5'],
|
'photo_keys' => ['required', 'array', 'min:1', 'max:5'],
|
||||||
'photo_keys.*' => ['required', 'string', 'max:500'],
|
'photo_keys.*' => ['required', 'string', 'max:500'],
|
||||||
'prices' => ['required', 'array', 'size:9'],
|
'prices' => ['required', 'array'],
|
||||||
'prices.*.type' => ['required', Rule::in(PriceType::values())],
|
'prices.*.type' => ['required', Rule::in(PriceType::values())],
|
||||||
'prices.*.price' => ['required', 'integer', 'min:0'],
|
'prices.*.price' => ['required', 'integer', 'min:0'],
|
||||||
];
|
];
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
use App\Services\S3PresignedService;
|
use App\Services\S3PresignedService;
|
||||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
class TransactionService
|
class TransactionService
|
||||||
{
|
{
|
||||||
@ -165,6 +166,12 @@ public function store(array $data): Order
|
|||||||
$negoPrice = ! empty($data['nego_price']) ? (int) $data['nego_price'] : null;
|
$negoPrice = ! empty($data['nego_price']) ? (int) $data['nego_price'] : null;
|
||||||
$totalAmount = $subtotal - $discount - ($negoPrice ?? 0);
|
$totalAmount = $subtotal - $discount - ($negoPrice ?? 0);
|
||||||
|
|
||||||
|
if ($totalAmount <= 0) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'discount' => 'Total harga tidak boleh nol atau kurang.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
$order = Order::create([
|
$order = Order::create([
|
||||||
'created_by_id' => auth()->id(),
|
'created_by_id' => auth()->id(),
|
||||||
'customer_id' => $data['customer_id'] ?? null,
|
'customer_id' => $data['customer_id'] ?? null,
|
||||||
@ -232,6 +239,12 @@ public function update(Order $order, array $data): Order
|
|||||||
$negoPrice = ! empty($data['nego_price']) ? (int) $data['nego_price'] : null;
|
$negoPrice = ! empty($data['nego_price']) ? (int) $data['nego_price'] : null;
|
||||||
$totalAmount = $subtotal - $discount - ($negoPrice ?? 0);
|
$totalAmount = $subtotal - $discount - ($negoPrice ?? 0);
|
||||||
|
|
||||||
|
if ($totalAmount <= 0) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'discount' => 'Total harga tidak boleh nol atau kurang.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($itemRows as &$row) {
|
foreach ($itemRows as &$row) {
|
||||||
$row['order_id'] = $order->id;
|
$row['order_id'] = $order->id;
|
||||||
}
|
}
|
||||||
@ -303,9 +316,13 @@ private function buildItemRows(array $items, string $stockType, string $priceTyp
|
|||||||
$variantIds = collect($items)->pluck('product_variant_id')->unique()->all();
|
$variantIds = collect($items)->pluck('product_variant_id')->unique()->all();
|
||||||
$variants = ProductVariant::query()
|
$variants = ProductVariant::query()
|
||||||
->whereKey($variantIds)
|
->whereKey($variantIds)
|
||||||
->with('productPrices:id,variant_id,type,price')
|
->with(['productPrices:id,variant_id,type,price', 'product:id,name'])
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
|
$variantLabels = $variants->mapWithKeys(fn (ProductVariant $v) => [
|
||||||
|
$v->id => $v->product->name.' - '.$v->name,
|
||||||
|
]);
|
||||||
|
|
||||||
$prices = $variants->mapWithKeys(function (ProductVariant $variant) use ($resolvedPriceType) {
|
$prices = $variants->mapWithKeys(function (ProductVariant $variant) use ($resolvedPriceType) {
|
||||||
$price = $variant->productPrices
|
$price = $variant->productPrices
|
||||||
->first(fn ($p) => $p->type === $resolvedPriceType);
|
->first(fn ($p) => $p->type === $resolvedPriceType);
|
||||||
@ -320,13 +337,28 @@ private function buildItemRows(array $items, string $stockType, string $priceTyp
|
|||||||
return [$variant->id => $price?->price ?? 0];
|
return [$variant->id => $price?->price ?? 0];
|
||||||
});
|
});
|
||||||
|
|
||||||
return collect($items)->map(function ($item) use ($now, $prices, $capitalPrices, $stockType, &$subtotal, &$totalCost) {
|
return collect($items)->map(function ($item) use ($now, $prices, $capitalPrices, $variantLabels, $stockType, &$subtotal, &$totalCost) {
|
||||||
$quantity = (int) $item['quantity'];
|
$quantity = (int) $item['quantity'];
|
||||||
$unitPrice = (int) ($prices[$item['product_variant_id']] ?? 0);
|
$unitPrice = (int) ($prices[$item['product_variant_id']] ?? 0);
|
||||||
|
$label = $variantLabels[$item['product_variant_id']] ?? ' Produk';
|
||||||
|
|
||||||
|
if ($unitPrice <= 0) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'items' => 'Harga untuk "'.$label.'" belum diatur.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
$itemSubtotal = $unitPrice * $quantity;
|
$itemSubtotal = $unitPrice * $quantity;
|
||||||
$subtotal += $itemSubtotal;
|
$subtotal += $itemSubtotal;
|
||||||
|
|
||||||
$capitalPrice = (int) ($capitalPrices[$item['product_variant_id']] ?? 0);
|
$capitalPrice = (int) ($capitalPrices[$item['product_variant_id']] ?? 0);
|
||||||
|
|
||||||
|
if ($capitalPrice <= 0) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'items' => 'Harga modal untuk "'.$label.'" belum diatur.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
$totalCost += $capitalPrice * $quantity;
|
$totalCost += $capitalPrice * $quantity;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|||||||
@ -40,10 +40,10 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
|
|||||||
'productVariants:id,product_id,name,stock,reject_stock,retail_stock',
|
'productVariants:id,product_id,name,stock,reject_stock,retail_stock',
|
||||||
'productVariants.productPrices:id,variant_id,type,price',
|
'productVariants.productPrices:id,variant_id,type,price',
|
||||||
])
|
])
|
||||||
->when($search, fn($q) => $q->where('name', 'like', "%{$search}%"))
|
->when($search, fn ($q) => $q->where('name', 'like', "%{$search}%"))
|
||||||
->when($filters['name'] ?? null, fn($q, $name) => $q->where('name', 'like', "%{$name}%"))
|
->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['status'] ?? null, fn ($q, $status) => $q->where('status', $status))
|
||||||
->when($filters['category'] ?? null, fn($q, $categoryId) => $q->whereHas('categories', function ($cq) use ($categoryId) {
|
->when($filters['category'] ?? null, fn ($q, $categoryId) => $q->whereHas('categories', function ($cq) use ($categoryId) {
|
||||||
$cq->where('categories.id', $categoryId);
|
$cq->where('categories.id', $categoryId);
|
||||||
}))
|
}))
|
||||||
->when(($filters['stock'] ?? null) === 'empty', function ($q) {
|
->when(($filters['stock'] ?? null) === 'empty', function ($q) {
|
||||||
@ -58,7 +58,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
|
|||||||
$paginator->getCollection()->each(function ($product) {
|
$paginator->getCollection()->each(function ($product) {
|
||||||
$product->productVariants->each(function ($variant) {
|
$product->productVariants->each(function ($variant) {
|
||||||
$media = $variant->getMedia('images');
|
$media = $variant->getMedia('images');
|
||||||
$variant->photo_urls = $media->map(fn($m) => $this->s3Service->getTemporaryUrl($m->getPath()))->toArray();
|
$variant->photo_urls = $media->map(fn ($m) => $this->s3Service->getTemporaryUrl($m->getPath()))->toArray();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ public function store(array $data): Product
|
|||||||
|
|
||||||
// Bulk insert variants
|
// Bulk insert variants
|
||||||
$now = now();
|
$now = now();
|
||||||
$variantRows = collect($data['variants'])->map(fn($v) => [
|
$variantRows = collect($data['variants'])->map(fn ($v) => [
|
||||||
'product_id' => $product->id,
|
'product_id' => $product->id,
|
||||||
'name' => $v['name'],
|
'name' => $v['name'],
|
||||||
'stock' => $v['stock'],
|
'stock' => $v['stock'],
|
||||||
@ -98,7 +98,7 @@ public function store(array $data): Product
|
|||||||
|
|
||||||
// Map variant name -> variant ID
|
// Map variant name -> variant ID
|
||||||
$insertedVariants = ProductVariant::where('product_id', $product->id)->get();
|
$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
|
// Bulk insert prices
|
||||||
$priceRows = [];
|
$priceRows = [];
|
||||||
@ -144,7 +144,7 @@ public function store(array $data): Product
|
|||||||
NotificationService::notify(
|
NotificationService::notify(
|
||||||
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
||||||
title: 'Produk Baru',
|
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'),
|
url: route('admin.master.products.index'),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -161,8 +161,8 @@ public function getForEdit(Product $product): array
|
|||||||
|
|
||||||
$variants = $product->productVariants->map(function (ProductVariant $variant) {
|
$variants = $product->productVariants->map(function (ProductVariant $variant) {
|
||||||
$media = $variant->getMedia('images');
|
$media = $variant->getMedia('images');
|
||||||
$photoKeys = $media->map(fn($m) => $m->getCustomProperty('s3_key') ?? $m->file_name)->toArray();
|
$photoKeys = $media->map(fn ($m) => $m->getCustomProperty('s3_key') ?? $m->file_name)->toArray();
|
||||||
$photoUrls = $media->map(fn($m) => $this->s3Service->getTemporaryUrl($m->getPath()))->toArray();
|
$photoUrls = $media->map(fn ($m) => $this->s3Service->getTemporaryUrl($m->getPath()))->toArray();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'id' => $variant->id,
|
'id' => $variant->id,
|
||||||
@ -172,7 +172,7 @@ public function getForEdit(Product $product): array
|
|||||||
'retail_stock' => $variant->retail_stock,
|
'retail_stock' => $variant->retail_stock,
|
||||||
'photo_keys' => $photoKeys,
|
'photo_keys' => $photoKeys,
|
||||||
'photo_urls' => $photoUrls,
|
'photo_urls' => $photoUrls,
|
||||||
'prices' => $variant->productPrices->map(fn($p) => [
|
'prices' => $variant->productPrices->map(fn ($p) => [
|
||||||
'type' => $p->type->value,
|
'type' => $p->type->value,
|
||||||
'price' => $p->price,
|
'price' => $p->price,
|
||||||
]),
|
]),
|
||||||
@ -230,7 +230,7 @@ public function update(Product $product, array $data): Product
|
|||||||
$existingVariantsMap = ProductVariant::whereIn('id', $existingVariantIds)
|
$existingVariantsMap = ProductVariant::whereIn('id', $existingVariantIds)
|
||||||
->with('media')
|
->with('media')
|
||||||
->get()
|
->get()
|
||||||
->mapWithKeys(fn($v) => [$v->id => $v]);
|
->mapWithKeys(fn ($v) => [$v->id => $v]);
|
||||||
|
|
||||||
// Collect old stock data + identify changed variants
|
// Collect old stock data + identify changed variants
|
||||||
$oldStockDataMap = [];
|
$oldStockDataMap = [];
|
||||||
@ -256,7 +256,7 @@ public function update(Product $product, array $data): Product
|
|||||||
// Bulk update changed variants (only changed ones, not all)
|
// Bulk update changed variants (only changed ones, not all)
|
||||||
if ($changedIds !== []) {
|
if ($changedIds !== []) {
|
||||||
$changedUpdates = collect($data['variants'])
|
$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) {
|
foreach ($changedUpdates as $variantData) {
|
||||||
$existingVariantsMap[$variantData['id']]->update([
|
$existingVariantsMap[$variantData['id']]->update([
|
||||||
@ -269,11 +269,11 @@ public function update(Product $product, array $data): Product
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bulk create new variants
|
// 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 = [];
|
$newVariantIdMap = [];
|
||||||
|
|
||||||
if ($newVariantsData->isNotEmpty()) {
|
if ($newVariantsData->isNotEmpty()) {
|
||||||
$newVariantRows = $newVariantsData->map(fn($v) => [
|
$newVariantRows = $newVariantsData->map(fn ($v) => [
|
||||||
'product_id' => $product->id,
|
'product_id' => $product->id,
|
||||||
'name' => $v['name'],
|
'name' => $v['name'],
|
||||||
'stock' => $v['stock'],
|
'stock' => $v['stock'],
|
||||||
@ -290,7 +290,7 @@ public function update(Product $product, array $data): Product
|
|||||||
->whereIn('name', $newVariantsData->pluck('name')->toArray())
|
->whereIn('name', $newVariantsData->pluck('name')->toArray())
|
||||||
->get();
|
->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
|
// Build variant_id lookup: existing by id, new by name
|
||||||
@ -354,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(
|
$this->stockMutationService->recordBulkAdjustment(
|
||||||
$changedModels,
|
$changedModels,
|
||||||
$oldStockDataMap,
|
$oldStockDataMap,
|
||||||
@ -388,7 +388,7 @@ public function update(Product $product, array $data): Product
|
|||||||
NotificationService::notify(
|
NotificationService::notify(
|
||||||
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
||||||
title: 'Produk Diperbarui',
|
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'),
|
url: route('admin.master.products.index'),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -414,7 +414,7 @@ public function destroy(Product $product): bool
|
|||||||
NotificationService::notify(
|
NotificationService::notify(
|
||||||
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
||||||
title: 'Produk Dihapus',
|
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'),
|
url: route('admin.master.products.index'),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -439,7 +439,7 @@ public function approve(Product $product): void
|
|||||||
NotificationService::notify(
|
NotificationService::notify(
|
||||||
roles: [Role::OWNER, Role::DEVELOPER],
|
roles: [Role::OWNER, Role::DEVELOPER],
|
||||||
title: 'Produk Disetujui',
|
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'),
|
url: route('admin.master.products.index'),
|
||||||
additionalUser: $product->createdBy ?? null,
|
additionalUser: $product->createdBy ?? null,
|
||||||
);
|
);
|
||||||
@ -455,7 +455,7 @@ public function reject(Product $product, string $reason = ''): void
|
|||||||
NotificationService::notify(
|
NotificationService::notify(
|
||||||
roles: [Role::OWNER, Role::DEVELOPER],
|
roles: [Role::OWNER, Role::DEVELOPER],
|
||||||
title: 'Produk Ditolak',
|
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'),
|
url: route('admin.master.products.index'),
|
||||||
additionalUser: $product->createdBy ?? null,
|
additionalUser: $product->createdBy ?? null,
|
||||||
);
|
);
|
||||||
@ -471,7 +471,7 @@ public function resubmit(Product $product): void
|
|||||||
NotificationService::notify(
|
NotificationService::notify(
|
||||||
roles: [Role::OWNER, Role::DEVELOPER],
|
roles: [Role::OWNER, Role::DEVELOPER],
|
||||||
title: 'Produk Diajukan Ulang',
|
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'),
|
url: route('admin.master.products.index'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,7 +50,7 @@ public function getForRestock(): array
|
|||||||
->first(fn ($price) => $price->type === PriceType::REJECT);
|
->first(fn ($price) => $price->type === PriceType::REJECT);
|
||||||
$variant->reject_price = $rejectPrice?->price ?? 0;
|
$variant->reject_price = $rejectPrice?->price ?? 0;
|
||||||
});
|
});
|
||||||
});
|
})->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getForTransaction(): array
|
public function getForTransaction(): array
|
||||||
@ -74,7 +74,7 @@ public function getForTransaction(): array
|
|||||||
$prices = $variant->productPrices->mapWithKeys(fn ($p) => [$p->type->value => $p->price]);
|
$prices = $variant->productPrices->mapWithKeys(fn ($p) => [$p->type->value => $p->price]);
|
||||||
$variant->prices = $prices;
|
$variant->prices = $prices;
|
||||||
});
|
});
|
||||||
});
|
})->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getForEdit(ProductVariant $variant): array
|
public function getForEdit(ProductVariant $variant): array
|
||||||
|
|||||||
@ -236,12 +236,16 @@ export default function TransactionCreate({
|
|||||||
|
|
||||||
const incrementQuantity = useCallback(
|
const incrementQuantity = useCallback(
|
||||||
(variantId: number, amount: number) => {
|
(variantId: number, amount: number) => {
|
||||||
|
if (amount > 0 && getUnitPrice(variantId) <= 0) {
|
||||||
|
toast.error('Harga produk ini belum diatur.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
setQuantities((prev) => ({
|
setQuantities((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[variantId]: Math.max(0, (prev[variantId] ?? 0) + amount),
|
[variantId]: Math.max(0, (prev[variantId] ?? 0) + amount),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
[],
|
[getUnitPrice],
|
||||||
);
|
);
|
||||||
|
|
||||||
const cartItems: CartLine[] = (() => {
|
const cartItems: CartLine[] = (() => {
|
||||||
@ -437,10 +441,14 @@ export default function TransactionCreate({
|
|||||||
)}{' '}
|
)}{' '}
|
||||||
pcs
|
pcs
|
||||||
·{' '}
|
·{' '}
|
||||||
{formatCurrency(
|
{getUnitPrice(variant.id) <= 0 ? (
|
||||||
stockType === 'reject'
|
<span className="text-destructive">Harga belum diatur</span>
|
||||||
? (variant.prices?.reject ?? 0)
|
) : (
|
||||||
: (variant.prices?.[priceType] ?? 0),
|
formatCurrency(
|
||||||
|
stockType === 'reject'
|
||||||
|
? (variant.prices?.reject ?? 0)
|
||||||
|
: (variant.prices?.[priceType] ?? 0),
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -490,6 +498,7 @@ export default function TransactionCreate({
|
|||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon"
|
size="icon"
|
||||||
|
disabled={getUnitPrice(variant.id) <= 0}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
incrementQuantity(
|
incrementQuantity(
|
||||||
variant.id,
|
variant.id,
|
||||||
@ -882,7 +891,11 @@ export default function TransactionCreate({
|
|||||||
!selectedProductId ||
|
!selectedProductId ||
|
||||||
Object.values(quantities).every(
|
Object.values(quantities).every(
|
||||||
(q) => q <= 0,
|
(q) => q <= 0,
|
||||||
)
|
) ||
|
||||||
|
Object.entries(quantities).some(
|
||||||
|
([id, q]) => q > 0 && getUnitPrice(Number(id)) <= 0,
|
||||||
|
) ||
|
||||||
|
total <= 0
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{processing
|
{processing
|
||||||
|
|||||||
@ -206,12 +206,16 @@ export default function TransactionEdit({
|
|||||||
|
|
||||||
const incrementQuantity = useCallback(
|
const incrementQuantity = useCallback(
|
||||||
(variantId: number, amount: number) => {
|
(variantId: number, amount: number) => {
|
||||||
|
if (amount > 0 && getUnitPrice(variantId) <= 0) {
|
||||||
|
toast.error('Harga produk ini belum diatur.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
setQuantities((prev) => ({
|
setQuantities((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[variantId]: Math.max(0, (prev[variantId] ?? 0) + amount),
|
[variantId]: Math.max(0, (prev[variantId] ?? 0) + amount),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
[],
|
[getUnitPrice],
|
||||||
);
|
);
|
||||||
|
|
||||||
const cartItems: CartLine[] = (() => {
|
const cartItems: CartLine[] = (() => {
|
||||||
@ -416,10 +420,14 @@ export default function TransactionEdit({
|
|||||||
)}{' '}
|
)}{' '}
|
||||||
pcs
|
pcs
|
||||||
·{' '}
|
·{' '}
|
||||||
{formatCurrency(
|
{getUnitPrice(variant.id) <= 0 ? (
|
||||||
stockType === 'reject'
|
<span className="text-destructive">Harga belum diatur</span>
|
||||||
? (variant.prices?.reject ?? 0)
|
) : (
|
||||||
: (variant.prices?.[priceType] ?? 0),
|
formatCurrency(
|
||||||
|
stockType === 'reject'
|
||||||
|
? (variant.prices?.reject ?? 0)
|
||||||
|
: (variant.prices?.[priceType] ?? 0),
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -469,6 +477,7 @@ export default function TransactionEdit({
|
|||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon"
|
size="icon"
|
||||||
|
disabled={getUnitPrice(variant.id) <= 0}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
incrementQuantity(
|
incrementQuantity(
|
||||||
variant.id,
|
variant.id,
|
||||||
@ -860,7 +869,11 @@ export default function TransactionEdit({
|
|||||||
uploading ||
|
uploading ||
|
||||||
Object.values(quantities).every(
|
Object.values(quantities).every(
|
||||||
(q) => q <= 0,
|
(q) => q <= 0,
|
||||||
)
|
) ||
|
||||||
|
Object.entries(quantities).some(
|
||||||
|
([id, q]) => q > 0 && getUnitPrice(Number(id)) <= 0,
|
||||||
|
) ||
|
||||||
|
total <= 0
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{processing
|
{processing
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user