feat: update material usage and stock fields to use decimal type for improved precision
This commit is contained in:
parent
cff231097a
commit
dd3a2aa1eb
@ -81,8 +81,8 @@ ### `raw_materials` → RawMaterial
|
||||
- Relations: rawMaterialPrices(HasMany→RawMaterialPrice)
|
||||
|
||||
### `raw_material_prices` → RawMaterialPrice
|
||||
`id` `raw_material_id`(FK→raw_materials) `variant`(200) `price`(uint) `stock`(uint,default:0) `created_at` `updated_at` `deleted_at`
|
||||
- Casts: price(int), stock(int)
|
||||
`id` `raw_material_id`(FK→raw_materials) `variant`(200) `price`(uint) `stock`(decimal(10,2),default:0) `created_at` `updated_at` `deleted_at`
|
||||
- Casts: price(int), stock(decimal:2)
|
||||
- GlobalScope: orderBy(variant)
|
||||
- Relations: rawMaterial(BelongsTo→RawMaterial,withTrashed), cuttingMaterials(HasMany→CuttingMaterial), purchaseItems(HasMany→PurchaseItem)
|
||||
- Accessor: photo_url → first media presigned S3 URL
|
||||
@ -199,8 +199,8 @@ ### `cutting_material_combinations` → CuttingMaterialCombination
|
||||
- Relations: cutting(BelongsTo→Cutting), cuttingMaterials(HasMany→CuttingMaterial,combination_id), user(BelongsTo→User)
|
||||
|
||||
### `cutting_materials` → CuttingMaterial
|
||||
`id` `user_id`(FK→users,null) `cutting_id`(FK→cuttings,null) `raw_material_price_id`(FK→raw_material_prices) `combination_id`(FK→cutting_material_combinations,null) `material_usage`(int) `material_result`(int,null) `created_at` `updated_at` `deleted_at`
|
||||
- Casts: material_usage(int), material_result(int)
|
||||
`id` `user_id`(FK→users,null) `cutting_id`(FK→cuttings,null) `raw_material_price_id`(FK→raw_material_prices) `combination_id`(FK→cutting_material_combinations,null) `material_usage`(decimal(10,2)) `material_result`(int,null) `created_at` `updated_at` `deleted_at`
|
||||
- Casts: material_usage(decimal:2), material_result(int)
|
||||
- Accessor: formatted_material_result → 'X.XXX', formatted_material_usage → 'X.XXX'
|
||||
- Relations: cutting(BelongsTo→Cutting), rawMaterialPrice(BelongsTo→RawMaterialPrice), combination(BelongsTo→CuttingMaterialCombination), user(BelongsTo→User)
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ public function rules(): array
|
||||
'cutting_result' => ['required', 'integer', 'min:1'],
|
||||
'materials' => ['required', 'array', 'min:1'],
|
||||
'materials.*.raw_material_price_id' => ['required', 'integer', Rule::exists('raw_material_prices', 'id')],
|
||||
'materials.*.material_usage' => ['required', 'integer', 'min:1'],
|
||||
'materials.*.material_usage' => ['required', 'numeric', 'min:0.01', 'decimal:0,2'],
|
||||
'materials.*.material_result' => ['required', 'integer'],
|
||||
'materials.*.combination_index' => ['nullable', 'integer'],
|
||||
'combinations' => ['nullable', 'array'],
|
||||
|
||||
@ -32,7 +32,7 @@ public function rules(): array
|
||||
'variants.*.id' => ['nullable', 'integer'],
|
||||
'variants.*.variant' => ['required', 'string', 'max:200'],
|
||||
'variants.*.price' => ['required', 'integer', 'min:0'],
|
||||
'variants.*.stock' => ['required', 'integer', 'min:0'],
|
||||
'variants.*.stock' => ['required', 'numeric', 'min:0', 'decimal:0,2'],
|
||||
'variants.*.photo_key' => ['required', 'string', 'max:500'],
|
||||
];
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ public function rules(): array
|
||||
return [
|
||||
'variant' => ['required', 'string', 'max:200'],
|
||||
'price' => ['required', 'integer', 'min:0'],
|
||||
'stock' => ['required', 'integer', 'min:0'],
|
||||
'stock' => ['required', 'numeric', 'min:0', 'decimal:0,2'],
|
||||
'photo_key' => ['required', 'string', 'max:500'],
|
||||
];
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ class CuttingMaterial extends Model
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'material_usage' => 'integer',
|
||||
'material_usage' => 'decimal:2',
|
||||
'material_result' => 'integer',
|
||||
];
|
||||
}
|
||||
@ -34,7 +34,7 @@ protected function formattedMaterialResult(): Attribute
|
||||
protected function formattedMaterialUsage(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => number_format($this->material_usage, 0, ',', '.'),
|
||||
get: fn () => number_format((float) $this->material_usage, 2, ',', '.'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'price' => 'integer',
|
||||
'stock' => 'integer',
|
||||
'stock' => 'decimal:2',
|
||||
];
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ protected function formattedPrice(): Attribute
|
||||
protected function formattedStock(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => number_format($this->stock, 0, ',', '.'),
|
||||
get: fn () => number_format((float) $this->stock, 2, ',', '.'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -259,7 +259,7 @@ public function store(array $data): Cutting
|
||||
$pricesMap = RawMaterialPrice::whereIn('id', $priceIds)->get()->keyBy('id');
|
||||
|
||||
foreach ($data['materials'] as $materialData) {
|
||||
$usage = (int) ($materialData['material_usage'] ?? 0);
|
||||
$usage = (float) ($materialData['material_usage'] ?? 0);
|
||||
if ($usage <= 0) {
|
||||
continue;
|
||||
}
|
||||
@ -320,7 +320,7 @@ public function store(array $data): Cutting
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
$usage = (int) ($materialData['material_usage'] ?? 0);
|
||||
$usage = (float) ($materialData['material_usage'] ?? 0);
|
||||
if ($price && $usage > 0) {
|
||||
$stockDecrementMap[$materialData['raw_material_price_id']] = ($stockDecrementMap[$materialData['raw_material_price_id']] ?? 0) + $usage;
|
||||
}
|
||||
@ -393,7 +393,7 @@ public function update(Cutting $cutting, array $data): Cutting
|
||||
$pricesMap = RawMaterialPrice::whereIn('id', $priceIds)->get()->keyBy('id');
|
||||
|
||||
foreach ($data['materials'] as $materialData) {
|
||||
$usage = (int) ($materialData['material_usage'] ?? 0);
|
||||
$usage = (float) ($materialData['material_usage'] ?? 0);
|
||||
if ($usage <= 0) {
|
||||
continue;
|
||||
}
|
||||
@ -452,7 +452,7 @@ public function update(Cutting $cutting, array $data): Cutting
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
$usage = (int) ($materialData['material_usage'] ?? 0);
|
||||
$usage = (float) ($materialData['material_usage'] ?? 0);
|
||||
if ($price && $usage > 0) {
|
||||
$stockDecrementMap[$materialData['raw_material_price_id']] = ($stockDecrementMap[$materialData['raw_material_price_id']] ?? 0) + $usage;
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ public function definition(): array
|
||||
'raw_material_id' => RawMaterial::factory(),
|
||||
'variant' => fake()->words(2, true),
|
||||
'price' => fake()->numberBetween(1000, 500000),
|
||||
'stock' => fake()->numberBetween(0, 1000),
|
||||
'stock' => round(fake()->randomFloat(2, 0, 1000), 2),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('raw_material_prices', function (Blueprint $table) {
|
||||
$table->decimal('stock', 10, 2)->default(0)->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('raw_material_prices', function (Blueprint $table) {
|
||||
$table->unsignedInteger('stock')->default(0)->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('cutting_materials', function (Blueprint $table) {
|
||||
$table->decimal('material_usage', 10, 2)->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('cutting_materials', function (Blueprint $table) {
|
||||
$table->integer('material_usage')->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -27,7 +27,7 @@ import type { CuttingCreateData } from './columns';
|
||||
|
||||
type MaterialState = {
|
||||
raw_material_price_id: number;
|
||||
material_usage: number;
|
||||
material_usage: string;
|
||||
material_result: number;
|
||||
combination_id: number | null;
|
||||
variant: string;
|
||||
@ -54,7 +54,7 @@ export default function CuttingCreate({ rawMaterials }: Props) {
|
||||
if (draft?.materials && draft.materials.length > 0) {
|
||||
return draft.materials.map((m) => ({
|
||||
raw_material_price_id: m.raw_material_price_id,
|
||||
material_usage: m.material_usage,
|
||||
material_usage: String(m.material_usage),
|
||||
material_result: m.material_result ?? 0,
|
||||
combination_id: m.combination_index ?? null,
|
||||
variant: m.variant,
|
||||
@ -185,7 +185,7 @@ export default function CuttingCreate({ rawMaterials }: Props) {
|
||||
...prev,
|
||||
{
|
||||
raw_material_price_id: price.id,
|
||||
material_usage: 0,
|
||||
material_usage: '0',
|
||||
material_result: 0,
|
||||
combination_id: null,
|
||||
variant: price.variant,
|
||||
@ -235,7 +235,7 @@ export default function CuttingCreate({ rawMaterials }: Props) {
|
||||
|
||||
return {
|
||||
raw_material_price_id: priceId,
|
||||
material_usage: 0,
|
||||
material_usage: '0',
|
||||
material_result: 0,
|
||||
combination_id: comboIndex,
|
||||
variant: foundPrice?.variant ?? '',
|
||||
@ -284,7 +284,7 @@ export default function CuttingCreate({ rawMaterials }: Props) {
|
||||
return materials.reduce((sum, m) => {
|
||||
const price = priceMap.get(m.raw_material_price_id);
|
||||
|
||||
return sum + (price ? price.price * m.material_usage : 0);
|
||||
return sum + (price ? price.price * (parseFloat(String(m.material_usage)) || 0) : 0);
|
||||
}, 0);
|
||||
}, [materials, priceMap]);
|
||||
|
||||
@ -304,7 +304,7 @@ export default function CuttingCreate({ rawMaterials }: Props) {
|
||||
cutting_result: cuttingResult,
|
||||
materials: materialsRef.current.map((m) => ({
|
||||
raw_material_price_id: m.raw_material_price_id,
|
||||
material_usage: m.material_usage,
|
||||
material_usage: parseFloat(String(m.material_usage)) || 0,
|
||||
material_result: m.material_result,
|
||||
combination_index: m.combination_id,
|
||||
})),
|
||||
@ -646,11 +646,13 @@ export default function CuttingCreate({ rawMaterials }: Props) {
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs text-muted-foreground whitespace-nowrap">Pemakaian <span className="text-destructive">*</span></span>
|
||||
<NumberInput
|
||||
className="flex-1"
|
||||
value={m.material_usage}
|
||||
onValueChange={(val) => updateMaterial(index, 'material_usage', val)}
|
||||
/>
|
||||
<Input
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
className="flex-1"
|
||||
value={m.material_usage}
|
||||
onChange={(e) => updateMaterial(index, 'material_usage', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<InputError message={errors[`materials.${index}.material_usage` as keyof typeof errors]} />
|
||||
{group.comboIndex === null && (
|
||||
@ -675,7 +677,7 @@ export default function CuttingCreate({ rawMaterials }: Props) {
|
||||
<SheetFooter>
|
||||
<div className="flex items-center justify-between border-t pt-4">
|
||||
<span className="text-sm">Total Pemakaian</span>
|
||||
<span className="text-sm font-semibold">{formatQuantity(materials.reduce((sum, m) => sum + m.material_usage, 0))}</span>
|
||||
<span className="text-sm font-semibold">{formatQuantity(materials.reduce((sum, m) => sum + (parseFloat(String(m.material_usage)) || 0), 0))}</span>
|
||||
</div>
|
||||
</SheetFooter>
|
||||
</SheetContent>
|
||||
|
||||
@ -26,7 +26,7 @@ import type { CuttingCreateData, CuttingForEdit } from './columns';
|
||||
type MaterialState = {
|
||||
id?: number;
|
||||
raw_material_price_id: number;
|
||||
material_usage: number;
|
||||
material_usage: string;
|
||||
material_result: number;
|
||||
combination_id: number | null;
|
||||
variant: string;
|
||||
@ -63,7 +63,7 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) {
|
||||
return {
|
||||
id: m.id,
|
||||
raw_material_price_id: m.raw_material_price_id,
|
||||
material_usage: m.material_usage,
|
||||
material_usage: String(m.material_usage),
|
||||
material_result: m.material_result ?? 0,
|
||||
combination_id:
|
||||
m.combination_id !== null
|
||||
@ -168,7 +168,7 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) {
|
||||
...prev,
|
||||
{
|
||||
raw_material_price_id: price.id,
|
||||
material_usage: 0,
|
||||
material_usage: '0',
|
||||
material_result: 0,
|
||||
combination_id: null,
|
||||
variant: price.variant,
|
||||
@ -218,7 +218,7 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) {
|
||||
|
||||
return {
|
||||
raw_material_price_id: priceId,
|
||||
material_usage: 0,
|
||||
material_usage: '0',
|
||||
material_result: 0,
|
||||
combination_id: comboIndex,
|
||||
variant: foundPrice?.variant ?? '',
|
||||
@ -262,7 +262,7 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) {
|
||||
return materials.reduce((sum, m) => {
|
||||
const price = priceMap.get(m.raw_material_price_id);
|
||||
|
||||
return sum + (price ? price.price * m.material_usage : 0);
|
||||
return sum + (price ? price.price * (parseFloat(String(m.material_usage)) || 0) : 0);
|
||||
}, 0);
|
||||
}, [materials, priceMap]);
|
||||
|
||||
@ -282,7 +282,7 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) {
|
||||
cutting_result: cuttingResult,
|
||||
materials: materialsRef.current.map((m) => ({
|
||||
raw_material_price_id: m.raw_material_price_id,
|
||||
material_usage: m.material_usage,
|
||||
material_usage: parseFloat(String(m.material_usage)) || 0,
|
||||
material_result: m.material_result,
|
||||
combination_index: m.combination_id,
|
||||
})),
|
||||
@ -624,11 +624,13 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) {
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs text-muted-foreground whitespace-nowrap">Pemakaian <span className="text-destructive">*</span></span>
|
||||
<NumberInput
|
||||
className="flex-1"
|
||||
value={m.material_usage}
|
||||
onValueChange={(val) => updateMaterial(index, 'material_usage', val)}
|
||||
/>
|
||||
<Input
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
className="flex-1"
|
||||
value={m.material_usage}
|
||||
onChange={(e) => updateMaterial(index, 'material_usage', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<InputError message={errors[`materials.${index}.material_usage` as keyof typeof errors]} />
|
||||
{group.comboIndex === null && (
|
||||
@ -653,7 +655,7 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) {
|
||||
<SheetFooter>
|
||||
<div className="flex items-center justify-between border-t pt-4">
|
||||
<span className="text-sm">Total Pemakaian</span>
|
||||
<span className="text-sm font-semibold">{formatQuantity(materials.reduce((sum, m) => sum + m.material_usage, 0))}</span>
|
||||
<span className="text-sm font-semibold">{formatQuantity(materials.reduce((sum, m) => sum + (parseFloat(String(m.material_usage)) || 0), 0))}</span>
|
||||
</div>
|
||||
</SheetFooter>
|
||||
</SheetContent>
|
||||
|
||||
@ -12,7 +12,6 @@ import { toast } from 'sonner';
|
||||
import { ConfirmDialog } from '@/components/dialogs';
|
||||
import { FileUpload } from '@/components/inputs';
|
||||
import { InputError } from '@/components/ui';
|
||||
import { NumberInput } from '@/components/inputs';
|
||||
import { RupiahInput } from '@/components/inputs';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@ -31,7 +30,7 @@ import {
|
||||
type VariantState = {
|
||||
variant: string;
|
||||
price: number;
|
||||
stock: number;
|
||||
stock: string;
|
||||
photo: string | null;
|
||||
photoUrl: string | null;
|
||||
uploading: boolean;
|
||||
@ -50,7 +49,7 @@ export default function RawMaterialCreate() {
|
||||
return draft.variants.map((v) => ({
|
||||
variant: v.variant,
|
||||
price: v.price,
|
||||
stock: v.stock,
|
||||
stock: String(v.stock),
|
||||
photo: v.photo ?? null,
|
||||
photoUrl: v.photo ? getTemporaryUrl(v.photo) : null,
|
||||
uploading: false,
|
||||
@ -61,7 +60,7 @@ export default function RawMaterialCreate() {
|
||||
{
|
||||
variant: '',
|
||||
price: 0,
|
||||
stock: 0,
|
||||
stock: '0',
|
||||
photo: null,
|
||||
photoUrl: null,
|
||||
uploading: false,
|
||||
@ -92,7 +91,7 @@ export default function RawMaterialCreate() {
|
||||
{
|
||||
variant: '',
|
||||
price: 0,
|
||||
stock: 0,
|
||||
stock: '0',
|
||||
photo: null,
|
||||
photoUrl: null,
|
||||
uploading: false,
|
||||
@ -178,7 +177,7 @@ export default function RawMaterialCreate() {
|
||||
variants: variantsRef.current.map((v) => ({
|
||||
variant: v.variant,
|
||||
price: Number(v.price),
|
||||
stock: Number(v.stock),
|
||||
stock: parseFloat(String(v.stock)) || 0,
|
||||
photo_key: v.photo,
|
||||
})),
|
||||
};
|
||||
@ -422,17 +421,19 @@ export default function RawMaterialCreate() {
|
||||
*
|
||||
</span>
|
||||
</Label>
|
||||
<NumberInput
|
||||
<Input
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
value={
|
||||
variant.stock
|
||||
}
|
||||
onValueChange={(
|
||||
val,
|
||||
onChange={(
|
||||
e,
|
||||
) =>
|
||||
updateVariant(
|
||||
variantIndex,
|
||||
'stock',
|
||||
val,
|
||||
e.target.value,
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
@ -12,7 +12,6 @@ import { toast } from 'sonner';
|
||||
import { ConfirmDialog } from '@/components/dialogs';
|
||||
import { FileUpload } from '@/components/inputs';
|
||||
import { InputError } from '@/components/ui';
|
||||
import { NumberInput } from '@/components/inputs';
|
||||
import { RupiahInput } from '@/components/inputs';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@ -37,7 +36,7 @@ type VariantState = {
|
||||
id: number | null;
|
||||
variant: string;
|
||||
price: number;
|
||||
stock: number;
|
||||
stock: string;
|
||||
photo: string | null;
|
||||
photoUrl: string | null;
|
||||
uploading: boolean;
|
||||
@ -58,7 +57,7 @@ export default function RawMaterialEdit({ rawMaterial }: Props) {
|
||||
id: v.id,
|
||||
variant: v.variant,
|
||||
price: v.price,
|
||||
stock: v.stock,
|
||||
stock: String(v.stock),
|
||||
photo: v.photo_key,
|
||||
photoUrl: v.photo_url,
|
||||
uploading: false,
|
||||
@ -73,7 +72,7 @@ export default function RawMaterialEdit({ rawMaterial }: Props) {
|
||||
id: null,
|
||||
variant: '',
|
||||
price: 0,
|
||||
stock: 0,
|
||||
stock: '0',
|
||||
photo: null,
|
||||
photoUrl: null,
|
||||
uploading: false,
|
||||
@ -106,7 +105,7 @@ export default function RawMaterialEdit({ rawMaterial }: Props) {
|
||||
id: null,
|
||||
variant: '',
|
||||
price: 0,
|
||||
stock: 0,
|
||||
stock: '0',
|
||||
photo: null,
|
||||
photoUrl: null,
|
||||
uploading: false,
|
||||
@ -193,7 +192,7 @@ export default function RawMaterialEdit({ rawMaterial }: Props) {
|
||||
id: v.id,
|
||||
variant: v.variant,
|
||||
price: Number(v.price),
|
||||
stock: Number(v.stock),
|
||||
stock: parseFloat(String(v.stock)) || 0,
|
||||
photo_key: v.photo,
|
||||
})),
|
||||
};
|
||||
@ -438,19 +437,19 @@ export default function RawMaterialEdit({ rawMaterial }: Props) {
|
||||
*
|
||||
</span>
|
||||
</Label>
|
||||
<NumberInput
|
||||
<Input
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
value={
|
||||
Number(
|
||||
variant.stock,
|
||||
) || 0
|
||||
variant.stock
|
||||
}
|
||||
onValueChange={(
|
||||
val,
|
||||
onChange={(
|
||||
e,
|
||||
) =>
|
||||
updateVariant(
|
||||
variantIndex,
|
||||
'stock',
|
||||
val,
|
||||
e.target.value,
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
@ -4,7 +4,6 @@ import { useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { FileUpload } from '@/components/inputs';
|
||||
import { InputError } from '@/components/ui';
|
||||
import { NumberInput } from '@/components/inputs';
|
||||
import { RupiahInput } from '@/components/inputs';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@ -28,7 +27,7 @@ type Props = {
|
||||
export default function RawMaterialVariantEdit({ variant }: Props) {
|
||||
const [variantName, setVariantName] = useState(variant.variant);
|
||||
const [price, setPrice] = useState(variant.price);
|
||||
const [stock, setStock] = useState(Number(variant.stock) || 0);
|
||||
const [stock, setStock] = useState(String(variant.stock) || '0');
|
||||
const [photo, setPhoto] = useState<string | null>(variant.photo_key);
|
||||
const [photoUrl, setPhotoUrl] = useState<string | null>(variant.photo_url);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
@ -37,7 +36,7 @@ export default function RawMaterialVariantEdit({ variant }: Props) {
|
||||
return {
|
||||
variant: variantName,
|
||||
price: Number(price),
|
||||
stock: Number(stock),
|
||||
stock: parseFloat(String(stock)) || 0,
|
||||
photo_key: photo,
|
||||
};
|
||||
}
|
||||
@ -101,9 +100,11 @@ export default function RawMaterialVariantEdit({ variant }: Props) {
|
||||
<Label>
|
||||
Stok <span className="text-destructive">*</span>
|
||||
</Label>
|
||||
<NumberInput
|
||||
<Input
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
value={stock}
|
||||
onValueChange={setStock}
|
||||
onChange={(e) => setStock(e.target.value)}
|
||||
/>
|
||||
<InputError message={errors.stock} />
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user