enh
This commit is contained in:
parent
32449b45f3
commit
042f874437
13 changed files with 1517 additions and 98 deletions
|
|
@ -8,7 +8,7 @@ export const BillItemSchema = z.object({
|
|||
product_name: z.string(),
|
||||
brand: z.string().nullable(),
|
||||
batch_id: z.number().int().nullable(),
|
||||
strips: z.number().int(),
|
||||
packs: z.number().int(),
|
||||
loose: z.number().int(),
|
||||
qty: z.number().int(),
|
||||
original_price: z.number(),
|
||||
|
|
@ -37,7 +37,7 @@ const BillItemInputSchema = z.object({
|
|||
product_name: z.string(),
|
||||
brand: z.string().nullable().optional(),
|
||||
batch_id: z.number().int().nullable().optional(),
|
||||
strips: z.number().int(),
|
||||
packs: z.number().int(),
|
||||
loose: z.number().int(),
|
||||
qty: z.number().int(),
|
||||
original_price: z.number(),
|
||||
|
|
|
|||
|
|
@ -67,8 +67,7 @@ function BillDetailsPage() {
|
|||
<DollarSign className="w-[14px] h-[14px]" />Payment Summary
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 gap-2 text-[13px]">
|
||||
<span className="text-slate-500">Subtotal</span><span className="font-medium text-right">₹{bill.subtotal.toFixed(2)}</span>
|
||||
<span className="text-slate-500">Tax ({bill.tax_rate}%)</span><span className="font-medium text-right">₹{bill.tax.toFixed(2)}</span>
|
||||
<span className="text-slate-500">Amount</span><span className="font-medium text-right">₹{bill.subtotal.toFixed(2)}</span>
|
||||
<span className="text-emerald-600">Discount</span><span className="font-medium text-right text-emerald-600">{bill.discount_percent}%</span>
|
||||
<span className="text-slate-900 font-semibold border-t border-slate-200 pt-1.5">Total</span>
|
||||
<span className="font-bold text-right text-blue-600 border-t border-slate-200 pt-1.5">₹{bill.total.toFixed(2)}</span>
|
||||
|
|
@ -85,7 +84,7 @@ function BillDetailsPage() {
|
|||
<thead>
|
||||
<tr className="text-[11px] font-semibold text-slate-600 uppercase tracking-wider border-b border-slate-200">
|
||||
<th className="text-left pb-2 px-2.5">Product</th>
|
||||
<th className="text-center pb-2 px-2.5">Strips</th>
|
||||
<th className="text-center pb-2 px-2.5">Packs</th>
|
||||
<th className="text-center pb-2 px-2.5">Loose</th>
|
||||
<th className="text-left pb-2 px-2.5">Price</th>
|
||||
<th className="text-right pb-2 px-2.5">Total</th>
|
||||
|
|
@ -98,7 +97,7 @@ function BillDetailsPage() {
|
|||
<div className="font-semibold text-[13px]">{item.product_name}</div>
|
||||
{item.brand && <div className="text-xs text-slate-500">{item.brand}</div>}
|
||||
</td>
|
||||
<td className="py-2.5 px-2.5 text-center">{item.strips}</td>
|
||||
<td className="py-2.5 px-2.5 text-center">{item.packs}</td>
|
||||
<td className="py-2.5 px-2.5 text-center">{item.loose}</td>
|
||||
<td className="py-2.5 px-2.5">
|
||||
{item.original_price !== item.selling_price ? (
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ interface BillItemForm {
|
|||
product_name: string;
|
||||
brand: string;
|
||||
batch_id: number | null;
|
||||
strips: number;
|
||||
packs: number;
|
||||
loose: number;
|
||||
qty: number;
|
||||
original_price: number;
|
||||
|
|
@ -110,12 +110,12 @@ function BillingPage() {
|
|||
product_name: product.name,
|
||||
brand: product.brand,
|
||||
batch_id: defaultBatch?.id ?? null,
|
||||
strips: product.units_per_strip ? 1 : 0,
|
||||
packs: 1,
|
||||
loose: 0,
|
||||
qty: product.units_per_strip ? product.units_per_strip * 1 : 1,
|
||||
qty: product.loose_sale_allowed ? (product.units_per_pack ?? 1) : 1,
|
||||
original_price: product.selling_price,
|
||||
selling_price: product.selling_price,
|
||||
total: product.units_per_strip ? product.units_per_strip * 1 * product.selling_price : product.selling_price,
|
||||
total: product.selling_price,
|
||||
batchOptions: productBatches.map((b) => ({
|
||||
id: b.id,
|
||||
batch_no: b.batch_no,
|
||||
|
|
@ -132,7 +132,7 @@ function BillingPage() {
|
|||
product_name: "",
|
||||
brand: "",
|
||||
batch_id: null,
|
||||
strips: 0,
|
||||
packs: 0,
|
||||
loose: 0,
|
||||
qty: 0,
|
||||
original_price: 0,
|
||||
|
|
@ -154,22 +154,25 @@ function BillingPage() {
|
|||
if (i !== idx) return item;
|
||||
|
||||
let updated = { ...item, [field]: value };
|
||||
const product = products?.find((p) => p.id === item.product_id);
|
||||
const ups = product?.units_per_pack ?? 0;
|
||||
const looseAllowed = product?.loose_sale_allowed ?? false;
|
||||
|
||||
const isQuantityField = field === "batch_id" || field === "selling_price" || field === "packs" || field === "loose";
|
||||
|
||||
if (isQuantityField) {
|
||||
if (field === "batch_id") {
|
||||
const batch = item.batchOptions.find((b) => b.id === Number(value));
|
||||
if (batch) updated.qty = updated.strips * (products?.find((p) => p.id === item.product_id)?.units_per_strip ?? 1) + updated.loose;
|
||||
if (!batch) return updated;
|
||||
}
|
||||
|
||||
if (field === "selling_price") {
|
||||
const ups = products?.find((p) => p.id === item.product_id)?.units_per_strip ?? 1;
|
||||
updated.qty = updated.strips * ups + updated.loose;
|
||||
updated.total = Number(updated.qty) * Number(updated.selling_price);
|
||||
if (looseAllowed) {
|
||||
updated.qty = Number(updated.packs) * ups + Number(updated.loose);
|
||||
updated.total = Number(updated.packs) * Number(updated.selling_price) + Number(updated.loose) * (Number(updated.selling_price) / ups);
|
||||
} else {
|
||||
updated.qty = Number(updated.packs);
|
||||
updated.total = Number(updated.packs) * Number(updated.selling_price);
|
||||
}
|
||||
|
||||
if (field === "strips" || field === "loose") {
|
||||
const ups = products?.find((p) => p.id === item.product_id)?.units_per_strip ?? 1;
|
||||
updated.qty = Number(updated.strips) * ups + Number(updated.loose);
|
||||
updated.total = Number(updated.qty) * Number(updated.selling_price);
|
||||
}
|
||||
|
||||
return updated;
|
||||
|
|
@ -183,21 +186,22 @@ function BillingPage() {
|
|||
setItems((prev) =>
|
||||
prev.map((item, i) => {
|
||||
if (i !== idx) return item;
|
||||
const ups = product.units_per_strip ?? 1;
|
||||
const strips = product.units_per_strip ? 1 : 0;
|
||||
const qty = strips * ups;
|
||||
const packs = 1;
|
||||
const loose = 0;
|
||||
const ups = product.units_per_pack ?? 0;
|
||||
const qty = product.loose_sale_allowed ? ups : 1;
|
||||
return {
|
||||
...item,
|
||||
product_id: product.id,
|
||||
product_name: product.name,
|
||||
brand: product.brand,
|
||||
batch_id: defaultBatch?.id ?? null,
|
||||
strips,
|
||||
loose: 0,
|
||||
packs,
|
||||
loose,
|
||||
qty,
|
||||
original_price: product.selling_price,
|
||||
selling_price: product.selling_price,
|
||||
total: qty * product.selling_price,
|
||||
total: product.loose_sale_allowed ? ups * product.selling_price : product.selling_price,
|
||||
batchOptions: productBatches.map((b) => ({
|
||||
id: b.id,
|
||||
batch_no: b.batch_no,
|
||||
|
|
@ -210,8 +214,7 @@ function BillingPage() {
|
|||
}
|
||||
|
||||
const subtotal = items.reduce((s, i) => s + i.total, 0);
|
||||
const tax = subtotal * 0.18;
|
||||
const total = subtotal + tax;
|
||||
const total = subtotal;
|
||||
|
||||
function handleGenerateBill() {
|
||||
if (!customerMobile) return;
|
||||
|
|
@ -224,8 +227,8 @@ function BillingPage() {
|
|||
customer_mobile: customerMobile,
|
||||
customer_name: customerName || null,
|
||||
subtotal,
|
||||
tax,
|
||||
tax_rate: 18,
|
||||
tax: 0,
|
||||
tax_rate: 0,
|
||||
total,
|
||||
discount: 0,
|
||||
discount_percent: 0,
|
||||
|
|
@ -235,7 +238,7 @@ function BillingPage() {
|
|||
product_name: i.product_name,
|
||||
brand: i.brand || null,
|
||||
batch_id: i.batch_id,
|
||||
strips: i.strips,
|
||||
packs: i.packs,
|
||||
loose: i.loose,
|
||||
qty: i.qty,
|
||||
original_price: i.original_price,
|
||||
|
|
@ -344,7 +347,7 @@ function BillingPage() {
|
|||
<tr className="text-[11px] font-semibold text-slate-600 uppercase tracking-wider border-b border-slate-200">
|
||||
<th className="text-left pb-2 px-2">Product</th>
|
||||
<th className="text-left pb-2 px-2">Batch</th>
|
||||
<th className="text-center pb-2 px-2 w-[70px]">Strips</th>
|
||||
<th className="text-center pb-2 px-2 w-[70px]">Packs</th>
|
||||
<th className="text-center pb-2 px-2 w-[70px]">Loose</th>
|
||||
<th className="text-right pb-2 px-2 w-[110px]">Price</th>
|
||||
<th className="text-right pb-2 px-2">Total</th>
|
||||
|
|
@ -394,11 +397,10 @@ function BillingPage() {
|
|||
<td className="py-2 px-2">
|
||||
<input
|
||||
type="number"
|
||||
value={item.strips}
|
||||
onChange={(e) => updateItem(idx, "strips", Number(e.target.value))}
|
||||
value={item.packs}
|
||||
onChange={(e) => updateItem(idx, "packs", Number(e.target.value))}
|
||||
className="w-full px-2 py-1.5 border border-slate-200 rounded text-xs text-center"
|
||||
min={0}
|
||||
disabled={!products?.find((p) => p.id === item.product_id)?.units_per_strip}
|
||||
/>
|
||||
</td>
|
||||
<td className="py-2 px-2">
|
||||
|
|
@ -408,6 +410,7 @@ function BillingPage() {
|
|||
onChange={(e) => updateItem(idx, "loose", Number(e.target.value))}
|
||||
className="w-full px-2 py-1.5 border border-slate-200 rounded text-xs text-center"
|
||||
min={0}
|
||||
disabled={!products?.find((p) => p.id === item.product_id)?.loose_sale_allowed}
|
||||
/>
|
||||
</td>
|
||||
<td className="py-2 px-2">
|
||||
|
|
@ -443,14 +446,6 @@ function BillingPage() {
|
|||
Bill Summary
|
||||
</h3>
|
||||
<div className="max-w-[360px] ml-auto">
|
||||
<div className="flex justify-between py-1.5 text-sm">
|
||||
<span className="text-slate-600">Subtotal</span>
|
||||
<span className="font-semibold">₹{subtotal.toFixed(2)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between py-1.5 text-[13px] text-slate-500">
|
||||
<span>Tax (18% GST)</span>
|
||||
<span>₹{tax.toFixed(2)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between py-1.5 text-lg font-bold border-t-2 border-slate-900 mt-1.5 pt-2.5">
|
||||
<span>Total</span>
|
||||
<span className="text-blue-600">₹{total.toFixed(2)}</span>
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ function ProductDetailsPage() {
|
|||
<DetailRow label="Brand Name" value={product.brand} />
|
||||
<DetailRow label="Category" value={product.category} />
|
||||
{product.units_per_strip && (
|
||||
<DetailRow label="Units per Strip" value={String(product.units_per_strip)} last />
|
||||
<DetailRow label="Units per Pack" value={String(product.units_per_pack)} last />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -84,6 +84,7 @@ function ProductDetailsPage() {
|
|||
<span className="text-xs font-semibold text-slate-600 uppercase tracking-wider">Inventory</span>
|
||||
</div>
|
||||
<div className="p-5">
|
||||
<DetailRow label="Loose Sale Allowed" value={product.loose_sale_allowed ? "Yes" : "No"} />
|
||||
<DetailRow label="Current Stock" value={`${product.size} units`} valueClass={stockClass} />
|
||||
<DetailRow label="Reorder Level" value={`${product.reorder_level} units`} last />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ const formSchema = CreateProductInput.extend({
|
|||
selling_price: z.coerce.number().min(0, "Must be ≥ 0"),
|
||||
size: z.coerce.number().int().default(0),
|
||||
reorder_level: z.coerce.number().int().default(0),
|
||||
units_per_strip: z.coerce.number().int().nullable().optional(),
|
||||
units_per_pack: z.coerce.number().int().nullable().optional(),
|
||||
loose_sale_allowed: z.coerce.boolean().default(false),
|
||||
});
|
||||
|
||||
type FormValues = z.infer<typeof formSchema>;
|
||||
|
|
@ -87,7 +88,8 @@ function AddProductPage() {
|
|||
selling_price: 0,
|
||||
size: 0,
|
||||
reorder_level: 0,
|
||||
units_per_strip: null,
|
||||
units_per_pack: null,
|
||||
loose_sale_allowed: false,
|
||||
hide_product_from_public: false,
|
||||
hide_price_from_public: false,
|
||||
compositions: [{ drug_name: "", quantity: "", unit_name: "" }],
|
||||
|
|
@ -99,9 +101,15 @@ function AddProductPage() {
|
|||
name: "compositions",
|
||||
});
|
||||
|
||||
const category = watch("category");
|
||||
const hideProduct = watch("hide_product_from_public");
|
||||
const showUnitsPerStrip = category === "Tablets" || category === "Capsules";
|
||||
const looseSaleAllowed = watch("loose_sale_allowed");
|
||||
const showUnitsPerPack = looseSaleAllowed;
|
||||
|
||||
useEffect(() => {
|
||||
if (!looseSaleAllowed) {
|
||||
setValue("units_per_pack", null);
|
||||
}
|
||||
}, [looseSaleAllowed, setValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hideProduct) {
|
||||
|
|
@ -122,7 +130,8 @@ function AddProductPage() {
|
|||
selling_price: existingProduct.selling_price,
|
||||
size: existingProduct.size,
|
||||
reorder_level: existingProduct.reorder_level,
|
||||
units_per_strip: existingProduct.units_per_strip,
|
||||
units_per_pack: existingProduct.units_per_pack,
|
||||
loose_sale_allowed: existingProduct.loose_sale_allowed,
|
||||
hide_product_from_public: existingProduct.hide_product_from_public,
|
||||
hide_price_from_public: existingProduct.hide_price_from_public,
|
||||
compositions: existingProduct.compositions.map((c) => ({
|
||||
|
|
@ -212,12 +221,19 @@ function AddProductPage() {
|
|||
/>
|
||||
</div>
|
||||
|
||||
{showUnitsPerStrip && (
|
||||
<div className="col-span-full">
|
||||
<Checkbox
|
||||
{...register("loose_sale_allowed")}
|
||||
label="Loose Sale Allowed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{showUnitsPerPack && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-900 mb-1.5">
|
||||
Units per Strip <span className="text-red-600">*</span>
|
||||
Units per Pack <span className="text-red-600">*</span>
|
||||
</label>
|
||||
<Input type="number" {...register("units_per_strip")} placeholder="e.g. 10" />
|
||||
<Input type="number" {...register("units_per_pack")} placeholder="e.g. 10" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -240,7 +256,7 @@ function AddProductPage() {
|
|||
{errors.unit_name && <p className="text-sm text-red-600 mt-1">{errors.unit_name.message}</p>}
|
||||
</div>
|
||||
|
||||
<div className={showUnitsPerStrip ? "" : "col-span-full"}>
|
||||
<div className={showUnitsPerPack ? "" : "col-span-full"}>
|
||||
<label className="block text-sm font-medium text-slate-900 mb-1.5">Distributor</label>
|
||||
<Select
|
||||
{...register("distributor_id")}
|
||||
|
|
|
|||
3
packages/data-manager-sqlite/drizzle/0008_calm_mimic.sql
Normal file
3
packages/data-manager-sqlite/drizzle/0008_calm_mimic.sql
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE `products` RENAME COLUMN "units_per_strip" TO "units_per_pack";--> statement-breakpoint
|
||||
ALTER TABLE `bill_items` RENAME COLUMN "strips" TO "packs";--> statement-breakpoint
|
||||
ALTER TABLE `products` ADD `loose_sale_allowed` integer DEFAULT false NOT NULL;
|
||||
1391
packages/data-manager-sqlite/drizzle/meta/0008_snapshot.json
Normal file
1391
packages/data-manager-sqlite/drizzle/meta/0008_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -57,6 +57,13 @@
|
|||
"when": 1779561725701,
|
||||
"tag": "0007_yellow_venom",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "6",
|
||||
"when": 1779616862450,
|
||||
"tag": "0008_calm_mimic",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ export type BillItem = {
|
|||
product_name: string
|
||||
brand: string | null
|
||||
batch_id: number | null
|
||||
strips: number
|
||||
packs: number
|
||||
loose: number
|
||||
qty: number
|
||||
original_price: number
|
||||
|
|
@ -55,7 +55,7 @@ export type BillsRepo = {
|
|||
product_name: string
|
||||
brand?: string | null
|
||||
batch_id?: number | null
|
||||
strips: number
|
||||
packs: number
|
||||
loose: number
|
||||
qty: number
|
||||
original_price: number
|
||||
|
|
@ -90,7 +90,7 @@ function getItemsForBill(billId: number): BillItem[] {
|
|||
product_name: r.productName,
|
||||
brand: r.brand,
|
||||
batch_id: r.batchId,
|
||||
strips: r.strips,
|
||||
packs: r.packs,
|
||||
loose: r.loose,
|
||||
qty: r.qty,
|
||||
original_price: r.originalPrice,
|
||||
|
|
@ -147,7 +147,7 @@ export function createBillsRepo(): { repo: BillsRepo } {
|
|||
productName: item.product_name,
|
||||
brand: item.brand ?? null,
|
||||
batchId: item.batch_id ?? null,
|
||||
strips: item.strips,
|
||||
packs: item.packs,
|
||||
loose: item.loose,
|
||||
qty: item.qty,
|
||||
originalPrice: item.original_price,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ interface ProductFields {
|
|||
selling_price: number
|
||||
size: number
|
||||
reorder_level: number
|
||||
units_per_strip: number | null
|
||||
units_per_pack: number | null
|
||||
loose_sale_allowed: boolean
|
||||
hide_product_from_public: boolean
|
||||
hide_price_from_public: boolean
|
||||
}
|
||||
|
|
@ -41,7 +42,7 @@ export type Product = ProductFields & {
|
|||
compositions: CompositionItem[]
|
||||
}
|
||||
|
||||
type ProductOptionalKeys = 'units_per_strip' | 'hide_product_from_public' | 'hide_price_from_public'
|
||||
type ProductOptionalKeys = 'units_per_pack' | 'loose_sale_allowed' | 'hide_product_from_public' | 'hide_price_from_public'
|
||||
type ProductRequiredKeys = Exclude<keyof ProductFields, ProductOptionalKeys>
|
||||
|
||||
export type CreateProductInput =
|
||||
|
|
@ -93,7 +94,8 @@ function toProduct(
|
|||
selling_price: row.sellingPrice,
|
||||
size: row.size,
|
||||
reorder_level: row.reorderLevel,
|
||||
units_per_strip: row.unitsPerStrip,
|
||||
units_per_pack: row.unitsPerPack,
|
||||
loose_sale_allowed: row.looseSaleAllowed,
|
||||
hide_product_from_public: row.hideProductFromPublic,
|
||||
hide_price_from_public: row.hidePriceFromPublic,
|
||||
}
|
||||
|
|
@ -192,7 +194,8 @@ export function createProductsRepo(): { repo: ProductsRepo } {
|
|||
sellingPrice: input.selling_price,
|
||||
size: input.size,
|
||||
reorderLevel: input.reorder_level,
|
||||
unitsPerStrip: input.units_per_strip ?? null,
|
||||
unitsPerPack: input.units_per_pack ?? null,
|
||||
looseSaleAllowed: input.loose_sale_allowed ?? false,
|
||||
hideProductFromPublic: input.hide_product_from_public ?? false,
|
||||
hidePriceFromPublic: input.hide_price_from_public ?? false,
|
||||
enterpriseId,
|
||||
|
|
@ -231,7 +234,8 @@ export function createProductsRepo(): { repo: ProductsRepo } {
|
|||
if (patch.selling_price !== undefined) setData.sellingPrice = patch.selling_price
|
||||
if (patch.size !== undefined) setData.size = patch.size
|
||||
if (patch.reorder_level !== undefined) setData.reorderLevel = patch.reorder_level
|
||||
if (patch.units_per_strip !== undefined) setData.unitsPerStrip = patch.units_per_strip ?? null
|
||||
if (patch.units_per_pack !== undefined) setData.unitsPerPack = patch.units_per_pack ?? null
|
||||
if (patch.loose_sale_allowed !== undefined) setData.looseSaleAllowed = patch.loose_sale_allowed
|
||||
if (patch.hide_product_from_public !== undefined) setData.hideProductFromPublic = patch.hide_product_from_public
|
||||
if (patch.hide_price_from_public !== undefined) setData.hidePriceFromPublic = patch.hide_price_from_public
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export const billItems = sqliteTable('bill_items', {
|
|||
batchId: integer('batch_id').references(() => stockBatches.id),
|
||||
productName: text('product_name').notNull(),
|
||||
brand: text('brand'),
|
||||
strips: integer('strips').notNull().default(0),
|
||||
packs: integer('packs').notNull().default(0),
|
||||
loose: integer('loose').notNull().default(0),
|
||||
qty: integer('qty').notNull(),
|
||||
originalPrice: real('original_price').notNull(),
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ export const products = sqliteTable('products', {
|
|||
sellingPrice: real('selling_price').notNull(),
|
||||
size: integer('size').notNull().default(0),
|
||||
reorderLevel: integer('reorder_level').notNull().default(0),
|
||||
unitsPerStrip: integer('units_per_strip'),
|
||||
unitsPerPack: integer('units_per_pack'),
|
||||
looseSaleAllowed: integer('loose_sale_allowed', { mode: 'boolean' }).notNull().default(false),
|
||||
hideProductFromPublic: integer('hide_product_from_public', { mode: 'boolean' }).notNull().default(false),
|
||||
hidePriceFromPublic: integer('hide_price_from_public', { mode: 'boolean' }).notNull().default(false),
|
||||
enterpriseId: integer('enterprise_id').references(() => enterprises.id),
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ export const ProductSchema = z.object({
|
|||
selling_price: z.number(),
|
||||
size: z.number().int(),
|
||||
reorder_level: z.number().int(),
|
||||
units_per_strip: z.number().int().nullable(),
|
||||
units_per_pack: z.number().int().nullable(),
|
||||
loose_sale_allowed: z.boolean(),
|
||||
hide_product_from_public: z.boolean(),
|
||||
hide_price_from_public: z.boolean(),
|
||||
compositions: z.array(CompositionItemSchema),
|
||||
|
|
@ -44,7 +45,8 @@ export const CreateProductInput = z.object({
|
|||
selling_price: shape.selling_price.min(0),
|
||||
size: shape.size.default(0),
|
||||
reorder_level: shape.reorder_level.default(0),
|
||||
units_per_strip: shape.units_per_strip.optional(),
|
||||
units_per_pack: shape.units_per_pack.optional(),
|
||||
loose_sale_allowed: z.boolean().default(false),
|
||||
hide_product_from_public: shape.hide_product_from_public.default(false),
|
||||
hide_price_from_public: shape.hide_price_from_public.default(false),
|
||||
compositions: z.array(CompositionInputSchema).default([]),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue