feat: add full/empty weight fields to Product and last_weighed_at to ProductInventory
Adds full_weight_g and empty_weight_g to ProductBase (inherited by Product and response models) so per-product package weight specs are captured. Adds last_weighed_at to ProductInventory to record when a package was last weighed. Wires up all fields through API schemas, frontend types, forms, and the product detail page (add/edit/display). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2b73dc63ac
commit
43fcba4de6
8 changed files with 255 additions and 16 deletions
|
|
@ -101,6 +101,7 @@ class InventoryCreate(SQLModel):
|
||||||
finished_at: Optional[date] = None
|
finished_at: Optional[date] = None
|
||||||
expiry_date: Optional[date] = None
|
expiry_date: Optional[date] = None
|
||||||
current_weight_g: Optional[float] = None
|
current_weight_g: Optional[float] = None
|
||||||
|
last_weighed_at: Optional[date] = None
|
||||||
notes: Optional[str] = None
|
notes: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -110,6 +111,7 @@ class InventoryUpdate(SQLModel):
|
||||||
finished_at: Optional[date] = None
|
finished_at: Optional[date] = None
|
||||||
expiry_date: Optional[date] = None
|
expiry_date: Optional[date] = None
|
||||||
current_weight_g: Optional[float] = None
|
current_weight_g: Optional[float] = None
|
||||||
|
last_weighed_at: Optional[date] = None
|
||||||
notes: Optional[str] = None
|
notes: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,8 @@ class ProductBase(SQLModel):
|
||||||
|
|
||||||
price_tier: PriceTier | None = None
|
price_tier: PriceTier | None = None
|
||||||
size_ml: float | None = Field(default=None, gt=0)
|
size_ml: float | None = Field(default=None, gt=0)
|
||||||
|
full_weight_g: float | None = Field(default=None, gt=0)
|
||||||
|
empty_weight_g: float | None = Field(default=None, gt=0)
|
||||||
pao_months: int | None = Field(default=None, ge=1, le=60)
|
pao_months: int | None = Field(default=None, ge=1, le=60)
|
||||||
|
|
||||||
inci: list[str] = Field(default_factory=list)
|
inci: list[str] = Field(default_factory=list)
|
||||||
|
|
@ -383,6 +385,7 @@ class ProductInventory(SQLModel, table=True):
|
||||||
finished_at: date | None = Field(default=None)
|
finished_at: date | None = Field(default=None)
|
||||||
expiry_date: date | None = Field(default=None)
|
expiry_date: date | None = Field(default=None)
|
||||||
current_weight_g: float | None = Field(default=None, gt=0)
|
current_weight_g: float | None = Field(default=None, gt=0)
|
||||||
|
last_weighed_at: date | None = Field(default=None)
|
||||||
notes: str | None = None
|
notes: str | None = None
|
||||||
|
|
||||||
created_at: datetime = Field(default_factory=utc_now, nullable=False)
|
created_at: datetime = Field(default_factory=utc_now, nullable=False)
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,9 @@ export const createInventory = (
|
||||||
productId: string,
|
productId: string,
|
||||||
body: Record<string, unknown>
|
body: Record<string, unknown>
|
||||||
): Promise<ProductInventory> => api.post(`/products/${productId}/inventory`, body);
|
): Promise<ProductInventory> => api.post(`/products/${productId}/inventory`, body);
|
||||||
|
export const updateInventory = (id: string, body: Record<string, unknown>): Promise<ProductInventory> =>
|
||||||
|
api.patch(`/inventory/${id}`, body);
|
||||||
|
export const deleteInventory = (id: string): Promise<void> => api.del(`/inventory/${id}`);
|
||||||
|
|
||||||
// ─── Routines ────────────────────────────────────────────────────────────────
|
// ─── Routines ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -668,6 +668,32 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<Label for="full_weight_g">Full weight (g)</Label>
|
||||||
|
<Input
|
||||||
|
id="full_weight_g"
|
||||||
|
name="full_weight_g"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.1"
|
||||||
|
placeholder="e.g. 120"
|
||||||
|
value={product?.full_weight_g ?? ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<Label for="empty_weight_g">Empty weight (g)</Label>
|
||||||
|
<Input
|
||||||
|
id="empty_weight_g"
|
||||||
|
name="empty_weight_g"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.1"
|
||||||
|
placeholder="e.g. 30"
|
||||||
|
value={product?.empty_weight_g ?? ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<Label for="pao_months">PAO (months)</Label>
|
<Label for="pao_months">PAO (months)</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ export interface ProductInventory {
|
||||||
finished_at?: string;
|
finished_at?: string;
|
||||||
expiry_date?: string;
|
expiry_date?: string;
|
||||||
current_weight_g?: number;
|
current_weight_g?: number;
|
||||||
|
last_weighed_at?: string;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
product?: Product;
|
product?: Product;
|
||||||
|
|
@ -127,6 +128,8 @@ export interface Product {
|
||||||
leave_on: boolean;
|
leave_on: boolean;
|
||||||
price_tier?: PriceTier;
|
price_tier?: PriceTier;
|
||||||
size_ml?: number;
|
size_ml?: number;
|
||||||
|
full_weight_g?: number;
|
||||||
|
empty_weight_g?: number;
|
||||||
pao_months?: number;
|
pao_months?: number;
|
||||||
inci: string[];
|
inci: string[];
|
||||||
actives?: ActiveIngredient[];
|
actives?: ActiveIngredient[];
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,11 @@
|
||||||
import { createInventory, deleteProduct, getProduct, updateProduct } from '$lib/api';
|
import {
|
||||||
|
createInventory,
|
||||||
|
deleteProduct,
|
||||||
|
getProduct,
|
||||||
|
updateProduct,
|
||||||
|
updateInventory as apiUpdateInventory,
|
||||||
|
deleteInventory as apiDeleteInventory
|
||||||
|
} from '$lib/api';
|
||||||
import { error, fail, redirect } from '@sveltejs/kit';
|
import { error, fail, redirect } from '@sveltejs/kit';
|
||||||
import type { Actions, PageServerLoad } from './$types';
|
import type { Actions, PageServerLoad } from './$types';
|
||||||
|
|
||||||
|
|
@ -125,6 +132,8 @@ export const actions: Actions = {
|
||||||
|
|
||||||
// Optional numbers
|
// Optional numbers
|
||||||
body.size_ml = parseOptionalFloat(form.get('size_ml') as string | null) ?? null;
|
body.size_ml = parseOptionalFloat(form.get('size_ml') as string | null) ?? null;
|
||||||
|
body.full_weight_g = parseOptionalFloat(form.get('full_weight_g') as string | null) ?? null;
|
||||||
|
body.empty_weight_g = parseOptionalFloat(form.get('empty_weight_g') as string | null) ?? null;
|
||||||
body.pao_months = parseOptionalInt(form.get('pao_months') as string | null) ?? null;
|
body.pao_months = parseOptionalInt(form.get('pao_months') as string | null) ?? null;
|
||||||
body.ph_min = parseOptionalFloat(form.get('ph_min') as string | null) ?? null;
|
body.ph_min = parseOptionalFloat(form.get('ph_min') as string | null) ?? null;
|
||||||
body.ph_max = parseOptionalFloat(form.get('ph_max') as string | null) ?? null;
|
body.ph_max = parseOptionalFloat(form.get('ph_max') as string | null) ?? null;
|
||||||
|
|
@ -199,10 +208,16 @@ export const actions: Actions = {
|
||||||
const body: Record<string, unknown> = {
|
const body: Record<string, unknown> = {
|
||||||
is_opened: form.get('is_opened') === 'true'
|
is_opened: form.get('is_opened') === 'true'
|
||||||
};
|
};
|
||||||
|
const openedAt = form.get('opened_at');
|
||||||
|
if (openedAt) body.opened_at = openedAt;
|
||||||
|
const finishedAt = form.get('finished_at');
|
||||||
|
if (finishedAt) body.finished_at = finishedAt;
|
||||||
const expiry = form.get('expiry_date');
|
const expiry = form.get('expiry_date');
|
||||||
if (expiry) body.expiry_date = expiry;
|
if (expiry) body.expiry_date = expiry;
|
||||||
const weight = form.get('current_weight_g');
|
const weight = form.get('current_weight_g');
|
||||||
if (weight) body.current_weight_g = Number(weight);
|
if (weight) body.current_weight_g = Number(weight);
|
||||||
|
const lastWeighed = form.get('last_weighed_at');
|
||||||
|
if (lastWeighed) body.last_weighed_at = lastWeighed;
|
||||||
const notes = form.get('notes');
|
const notes = form.get('notes');
|
||||||
if (notes) body.notes = notes;
|
if (notes) body.notes = notes;
|
||||||
try {
|
try {
|
||||||
|
|
@ -211,5 +226,44 @@ export const actions: Actions = {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return fail(500, { error: (e as Error).message });
|
return fail(500, { error: (e as Error).message });
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateInventory: async ({ request }) => {
|
||||||
|
const form = await request.formData();
|
||||||
|
const inventoryId = form.get('inventory_id') as string;
|
||||||
|
if (!inventoryId) return fail(400, { error: 'Missing inventory_id' });
|
||||||
|
const body: Record<string, unknown> = {
|
||||||
|
is_opened: form.get('is_opened') === 'true'
|
||||||
|
};
|
||||||
|
const openedAt = form.get('opened_at');
|
||||||
|
body.opened_at = openedAt || null;
|
||||||
|
const finishedAt = form.get('finished_at');
|
||||||
|
body.finished_at = finishedAt || null;
|
||||||
|
const expiry = form.get('expiry_date');
|
||||||
|
body.expiry_date = expiry || null;
|
||||||
|
const weight = form.get('current_weight_g');
|
||||||
|
body.current_weight_g = weight ? Number(weight) : null;
|
||||||
|
const lastWeighed = form.get('last_weighed_at');
|
||||||
|
body.last_weighed_at = lastWeighed || null;
|
||||||
|
const notes = form.get('notes');
|
||||||
|
body.notes = notes || null;
|
||||||
|
try {
|
||||||
|
await apiUpdateInventory(inventoryId, body);
|
||||||
|
return { inventoryUpdated: true };
|
||||||
|
} catch (e) {
|
||||||
|
return fail(500, { error: (e as Error).message });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteInventory: async ({ request }) => {
|
||||||
|
const form = await request.formData();
|
||||||
|
const inventoryId = form.get('inventory_id') as string;
|
||||||
|
if (!inventoryId) return fail(400, { error: 'Missing inventory_id' });
|
||||||
|
try {
|
||||||
|
await apiDeleteInventory(inventoryId);
|
||||||
|
return { inventoryDeleted: true };
|
||||||
|
} catch (e) {
|
||||||
|
return fail(500, { error: (e as Error).message });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
let { product } = $derived(data);
|
let { product } = $derived(data);
|
||||||
|
|
||||||
let showInventoryForm = $state(false);
|
let showInventoryForm = $state(false);
|
||||||
|
let editingInventoryId = $state<string | null>(null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head><title>{product.name} — innercontext</title></svelte:head>
|
<svelte:head><title>{product.name} — innercontext</title></svelte:head>
|
||||||
|
|
@ -54,11 +55,29 @@
|
||||||
{#if form?.inventoryAdded}
|
{#if form?.inventoryAdded}
|
||||||
<div class="rounded-md bg-green-50 px-4 py-3 text-sm text-green-700">Package added.</div>
|
<div class="rounded-md bg-green-50 px-4 py-3 text-sm text-green-700">Package added.</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if form?.inventoryUpdated}
|
||||||
|
<div class="rounded-md bg-green-50 px-4 py-3 text-sm text-green-700">Package updated.</div>
|
||||||
|
{/if}
|
||||||
|
{#if form?.inventoryDeleted}
|
||||||
|
<div class="rounded-md bg-green-50 px-4 py-3 text-sm text-green-700">Package deleted.</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if showInventoryForm}
|
{#if showInventoryForm}
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent class="pt-4">
|
<CardContent class="pt-4">
|
||||||
<form method="POST" action="?/addInventory" use:enhance class="grid grid-cols-2 gap-4">
|
<form method="POST" action="?/addInventory" use:enhance class="grid grid-cols-2 gap-4">
|
||||||
|
<div class="col-span-2 flex items-center gap-2">
|
||||||
|
<input type="checkbox" id="add_is_opened" name="is_opened" value="true" class="h-4 w-4" />
|
||||||
|
<Label for="add_is_opened">Already opened</Label>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<Label for="add_opened_at">Opened date</Label>
|
||||||
|
<Input id="add_opened_at" name="opened_at" type="date" />
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<Label for="add_finished_at">Finished date</Label>
|
||||||
|
<Input id="add_finished_at" name="finished_at" type="date" />
|
||||||
|
</div>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<Label for="expiry_date">Expiry date</Label>
|
<Label for="expiry_date">Expiry date</Label>
|
||||||
<Input id="expiry_date" name="expiry_date" type="date" />
|
<Input id="expiry_date" name="expiry_date" type="date" />
|
||||||
|
|
@ -67,6 +86,10 @@
|
||||||
<Label for="current_weight_g">Current weight (g)</Label>
|
<Label for="current_weight_g">Current weight (g)</Label>
|
||||||
<Input id="current_weight_g" name="current_weight_g" type="number" min="0" />
|
<Input id="current_weight_g" name="current_weight_g" type="number" min="0" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<Label for="add_last_weighed_at">Last weighed</Label>
|
||||||
|
<Input id="add_last_weighed_at" name="last_weighed_at" type="date" />
|
||||||
|
</div>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<Label for="notes">Notes</Label>
|
<Label for="notes">Notes</Label>
|
||||||
<Input id="notes" name="notes" />
|
<Input id="notes" name="notes" />
|
||||||
|
|
@ -74,7 +97,6 @@
|
||||||
<div class="flex items-end">
|
<div class="flex items-end">
|
||||||
<Button type="submit" size="sm">Add</Button>
|
<Button type="submit" size="sm">Add</Button>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="is_opened" value="false" />
|
|
||||||
</form>
|
</form>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -82,21 +104,141 @@
|
||||||
|
|
||||||
{#if product.inventory.length}
|
{#if product.inventory.length}
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
{#each product.inventory as pkg}
|
{#each product.inventory as pkg (pkg.id)}
|
||||||
<div class="flex items-center justify-between rounded-md border border-border px-4 py-3 text-sm">
|
<div class="rounded-md border border-border text-sm">
|
||||||
<div class="space-x-3">
|
<div class="flex items-center justify-between px-4 py-3">
|
||||||
<Badge variant={pkg.is_opened ? 'default' : 'secondary'}>
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
{pkg.is_opened ? 'Open' : 'Sealed'}
|
<Badge variant={pkg.is_opened ? 'default' : 'secondary'}>
|
||||||
</Badge>
|
{pkg.is_opened ? 'Open' : 'Sealed'}
|
||||||
{#if pkg.expiry_date}
|
</Badge>
|
||||||
<span class="text-muted-foreground">Exp: {pkg.expiry_date}</span>
|
{#if pkg.finished_at}
|
||||||
{/if}
|
<Badge variant="outline">Finished</Badge>
|
||||||
{#if pkg.current_weight_g}
|
{/if}
|
||||||
<span class="text-muted-foreground">{pkg.current_weight_g}g remaining</span>
|
{#if pkg.expiry_date}
|
||||||
{/if}
|
<span class="text-muted-foreground">Exp: {pkg.expiry_date.slice(0, 10)}</span>
|
||||||
|
{/if}
|
||||||
|
{#if pkg.opened_at}
|
||||||
|
<span class="text-muted-foreground">Opened: {pkg.opened_at.slice(0, 10)}</span>
|
||||||
|
{/if}
|
||||||
|
{#if pkg.finished_at}
|
||||||
|
<span class="text-muted-foreground">Finished: {pkg.finished_at.slice(0, 10)}</span>
|
||||||
|
{/if}
|
||||||
|
{#if pkg.current_weight_g}
|
||||||
|
<span class="text-muted-foreground">{pkg.current_weight_g}g remaining</span>
|
||||||
|
{/if}
|
||||||
|
{#if pkg.last_weighed_at}
|
||||||
|
<span class="text-muted-foreground">Weighed: {pkg.last_weighed_at.slice(0, 10)}</span>
|
||||||
|
{/if}
|
||||||
|
{#if pkg.notes}
|
||||||
|
<span class="text-muted-foreground">{pkg.notes}</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onclick={() => (editingInventoryId = editingInventoryId === pkg.id ? null : pkg.id)}
|
||||||
|
>
|
||||||
|
{editingInventoryId === pkg.id ? 'Cancel' : 'Edit'}
|
||||||
|
</Button>
|
||||||
|
<form
|
||||||
|
method="POST"
|
||||||
|
action="?/deleteInventory"
|
||||||
|
use:enhance
|
||||||
|
onsubmit={(e) => { if (!confirm('Delete this package?')) e.preventDefault(); }}
|
||||||
|
>
|
||||||
|
<input type="hidden" name="inventory_id" value={pkg.id} />
|
||||||
|
<Button type="submit" variant="ghost" size="sm" class="text-destructive hover:text-destructive">×</Button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if pkg.notes}
|
|
||||||
<span class="text-muted-foreground">{pkg.notes}</span>
|
{#if editingInventoryId === pkg.id}
|
||||||
|
<div class="border-t px-4 py-3">
|
||||||
|
<form
|
||||||
|
method="POST"
|
||||||
|
action="?/updateInventory"
|
||||||
|
use:enhance={() => {
|
||||||
|
return async ({ result, update }) => {
|
||||||
|
await update();
|
||||||
|
if (result.type === 'success') editingInventoryId = null;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
class="grid grid-cols-2 gap-4"
|
||||||
|
>
|
||||||
|
<input type="hidden" name="inventory_id" value={pkg.id} />
|
||||||
|
<div class="col-span-2 flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="edit_is_opened_{pkg.id}"
|
||||||
|
name="is_opened"
|
||||||
|
value="true"
|
||||||
|
checked={pkg.is_opened}
|
||||||
|
class="h-4 w-4"
|
||||||
|
/>
|
||||||
|
<Label for="edit_is_opened_{pkg.id}">Already opened</Label>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<Label for="edit_opened_at_{pkg.id}">Opened date</Label>
|
||||||
|
<Input
|
||||||
|
id="edit_opened_at_{pkg.id}"
|
||||||
|
name="opened_at"
|
||||||
|
type="date"
|
||||||
|
value={pkg.opened_at?.slice(0, 10) ?? ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<Label for="edit_finished_at_{pkg.id}">Finished date</Label>
|
||||||
|
<Input
|
||||||
|
id="edit_finished_at_{pkg.id}"
|
||||||
|
name="finished_at"
|
||||||
|
type="date"
|
||||||
|
value={pkg.finished_at?.slice(0, 10) ?? ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<Label for="edit_expiry_{pkg.id}">Expiry date</Label>
|
||||||
|
<Input
|
||||||
|
id="edit_expiry_{pkg.id}"
|
||||||
|
name="expiry_date"
|
||||||
|
type="date"
|
||||||
|
value={pkg.expiry_date?.slice(0, 10) ?? ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<Label for="edit_weight_{pkg.id}">Current weight (g)</Label>
|
||||||
|
<Input
|
||||||
|
id="edit_weight_{pkg.id}"
|
||||||
|
name="current_weight_g"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
value={pkg.current_weight_g ?? ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<Label for="edit_last_weighed_{pkg.id}">Last weighed</Label>
|
||||||
|
<Input
|
||||||
|
id="edit_last_weighed_{pkg.id}"
|
||||||
|
name="last_weighed_at"
|
||||||
|
type="date"
|
||||||
|
value={pkg.last_weighed_at?.slice(0, 10) ?? ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<Label for="edit_notes_{pkg.id}">Notes</Label>
|
||||||
|
<Input id="edit_notes_{pkg.id}" name="notes" value={pkg.notes ?? ''} />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-end gap-2">
|
||||||
|
<Button type="submit" size="sm">Save</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onclick={() => (editingInventoryId = null)}
|
||||||
|
>Cancel</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,12 @@ export const actions: Actions = {
|
||||||
const size_ml = parseOptionalFloat(form.get('size_ml') as string | null);
|
const size_ml = parseOptionalFloat(form.get('size_ml') as string | null);
|
||||||
if (size_ml !== undefined) payload.size_ml = size_ml;
|
if (size_ml !== undefined) payload.size_ml = size_ml;
|
||||||
|
|
||||||
|
const full_weight_g = parseOptionalFloat(form.get('full_weight_g') as string | null);
|
||||||
|
if (full_weight_g !== undefined) payload.full_weight_g = full_weight_g;
|
||||||
|
|
||||||
|
const empty_weight_g = parseOptionalFloat(form.get('empty_weight_g') as string | null);
|
||||||
|
if (empty_weight_g !== undefined) payload.empty_weight_g = empty_weight_g;
|
||||||
|
|
||||||
const pao_months = parseOptionalInt(form.get('pao_months') as string | null);
|
const pao_months = parseOptionalInt(form.get('pao_months') as string | null);
|
||||||
if (pao_months !== undefined) payload.pao_months = pao_months;
|
if (pao_months !== undefined) payload.pao_months = pao_months;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue