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(),
|
product_name: z.string(),
|
||||||
brand: z.string().nullable(),
|
brand: z.string().nullable(),
|
||||||
batch_id: z.number().int().nullable(),
|
batch_id: z.number().int().nullable(),
|
||||||
strips: z.number().int(),
|
packs: z.number().int(),
|
||||||
loose: z.number().int(),
|
loose: z.number().int(),
|
||||||
qty: z.number().int(),
|
qty: z.number().int(),
|
||||||
original_price: z.number(),
|
original_price: z.number(),
|
||||||
|
|
@ -37,7 +37,7 @@ const BillItemInputSchema = z.object({
|
||||||
product_name: z.string(),
|
product_name: z.string(),
|
||||||
brand: z.string().nullable().optional(),
|
brand: z.string().nullable().optional(),
|
||||||
batch_id: z.number().int().nullable().optional(),
|
batch_id: z.number().int().nullable().optional(),
|
||||||
strips: z.number().int(),
|
packs: z.number().int(),
|
||||||
loose: z.number().int(),
|
loose: z.number().int(),
|
||||||
qty: z.number().int(),
|
qty: z.number().int(),
|
||||||
original_price: z.number(),
|
original_price: z.number(),
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,7 @@ function BillDetailsPage() {
|
||||||
<DollarSign className="w-[14px] h-[14px]" />Payment Summary
|
<DollarSign className="w-[14px] h-[14px]" />Payment Summary
|
||||||
</h3>
|
</h3>
|
||||||
<div className="grid grid-cols-2 gap-2 text-[13px]">
|
<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">Amount</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-emerald-600">Discount</span><span className="font-medium text-right text-emerald-600">{bill.discount_percent}%</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="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>
|
<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>
|
<thead>
|
||||||
<tr className="text-[11px] font-semibold text-slate-600 uppercase tracking-wider border-b border-slate-200">
|
<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-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-center pb-2 px-2.5">Loose</th>
|
||||||
<th className="text-left pb-2 px-2.5">Price</th>
|
<th className="text-left pb-2 px-2.5">Price</th>
|
||||||
<th className="text-right pb-2 px-2.5">Total</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>
|
<div className="font-semibold text-[13px]">{item.product_name}</div>
|
||||||
{item.brand && <div className="text-xs text-slate-500">{item.brand}</div>}
|
{item.brand && <div className="text-xs text-slate-500">{item.brand}</div>}
|
||||||
</td>
|
</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 text-center">{item.loose}</td>
|
||||||
<td className="py-2.5 px-2.5">
|
<td className="py-2.5 px-2.5">
|
||||||
{item.original_price !== item.selling_price ? (
|
{item.original_price !== item.selling_price ? (
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ interface BillItemForm {
|
||||||
product_name: string;
|
product_name: string;
|
||||||
brand: string;
|
brand: string;
|
||||||
batch_id: number | null;
|
batch_id: number | null;
|
||||||
strips: number;
|
packs: number;
|
||||||
loose: number;
|
loose: number;
|
||||||
qty: number;
|
qty: number;
|
||||||
original_price: number;
|
original_price: number;
|
||||||
|
|
@ -110,12 +110,12 @@ function BillingPage() {
|
||||||
product_name: product.name,
|
product_name: product.name,
|
||||||
brand: product.brand,
|
brand: product.brand,
|
||||||
batch_id: defaultBatch?.id ?? null,
|
batch_id: defaultBatch?.id ?? null,
|
||||||
strips: product.units_per_strip ? 1 : 0,
|
packs: 1,
|
||||||
loose: 0,
|
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,
|
original_price: product.selling_price,
|
||||||
selling_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) => ({
|
batchOptions: productBatches.map((b) => ({
|
||||||
id: b.id,
|
id: b.id,
|
||||||
batch_no: b.batch_no,
|
batch_no: b.batch_no,
|
||||||
|
|
@ -132,7 +132,7 @@ function BillingPage() {
|
||||||
product_name: "",
|
product_name: "",
|
||||||
brand: "",
|
brand: "",
|
||||||
batch_id: null,
|
batch_id: null,
|
||||||
strips: 0,
|
packs: 0,
|
||||||
loose: 0,
|
loose: 0,
|
||||||
qty: 0,
|
qty: 0,
|
||||||
original_price: 0,
|
original_price: 0,
|
||||||
|
|
@ -154,22 +154,25 @@ function BillingPage() {
|
||||||
if (i !== idx) return item;
|
if (i !== idx) return item;
|
||||||
|
|
||||||
let updated = { ...item, [field]: value };
|
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") {
|
if (field === "batch_id") {
|
||||||
const batch = item.batchOptions.find((b) => b.id === Number(value));
|
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") {
|
if (looseAllowed) {
|
||||||
const ups = products?.find((p) => p.id === item.product_id)?.units_per_strip ?? 1;
|
updated.qty = Number(updated.packs) * ups + Number(updated.loose);
|
||||||
updated.qty = updated.strips * ups + updated.loose;
|
updated.total = Number(updated.packs) * Number(updated.selling_price) + Number(updated.loose) * (Number(updated.selling_price) / ups);
|
||||||
updated.total = Number(updated.qty) * Number(updated.selling_price);
|
} 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;
|
return updated;
|
||||||
|
|
@ -183,21 +186,22 @@ function BillingPage() {
|
||||||
setItems((prev) =>
|
setItems((prev) =>
|
||||||
prev.map((item, i) => {
|
prev.map((item, i) => {
|
||||||
if (i !== idx) return item;
|
if (i !== idx) return item;
|
||||||
const ups = product.units_per_strip ?? 1;
|
const packs = 1;
|
||||||
const strips = product.units_per_strip ? 1 : 0;
|
const loose = 0;
|
||||||
const qty = strips * ups;
|
const ups = product.units_per_pack ?? 0;
|
||||||
|
const qty = product.loose_sale_allowed ? ups : 1;
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
product_id: product.id,
|
product_id: product.id,
|
||||||
product_name: product.name,
|
product_name: product.name,
|
||||||
brand: product.brand,
|
brand: product.brand,
|
||||||
batch_id: defaultBatch?.id ?? null,
|
batch_id: defaultBatch?.id ?? null,
|
||||||
strips,
|
packs,
|
||||||
loose: 0,
|
loose,
|
||||||
qty,
|
qty,
|
||||||
original_price: product.selling_price,
|
original_price: product.selling_price,
|
||||||
selling_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) => ({
|
batchOptions: productBatches.map((b) => ({
|
||||||
id: b.id,
|
id: b.id,
|
||||||
batch_no: b.batch_no,
|
batch_no: b.batch_no,
|
||||||
|
|
@ -210,8 +214,7 @@ function BillingPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const subtotal = items.reduce((s, i) => s + i.total, 0);
|
const subtotal = items.reduce((s, i) => s + i.total, 0);
|
||||||
const tax = subtotal * 0.18;
|
const total = subtotal;
|
||||||
const total = subtotal + tax;
|
|
||||||
|
|
||||||
function handleGenerateBill() {
|
function handleGenerateBill() {
|
||||||
if (!customerMobile) return;
|
if (!customerMobile) return;
|
||||||
|
|
@ -224,8 +227,8 @@ function BillingPage() {
|
||||||
customer_mobile: customerMobile,
|
customer_mobile: customerMobile,
|
||||||
customer_name: customerName || null,
|
customer_name: customerName || null,
|
||||||
subtotal,
|
subtotal,
|
||||||
tax,
|
tax: 0,
|
||||||
tax_rate: 18,
|
tax_rate: 0,
|
||||||
total,
|
total,
|
||||||
discount: 0,
|
discount: 0,
|
||||||
discount_percent: 0,
|
discount_percent: 0,
|
||||||
|
|
@ -235,7 +238,7 @@ function BillingPage() {
|
||||||
product_name: i.product_name,
|
product_name: i.product_name,
|
||||||
brand: i.brand || null,
|
brand: i.brand || null,
|
||||||
batch_id: i.batch_id,
|
batch_id: i.batch_id,
|
||||||
strips: i.strips,
|
packs: i.packs,
|
||||||
loose: i.loose,
|
loose: i.loose,
|
||||||
qty: i.qty,
|
qty: i.qty,
|
||||||
original_price: i.original_price,
|
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">
|
<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">Product</th>
|
||||||
<th className="text-left pb-2 px-2">Batch</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-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 w-[110px]">Price</th>
|
||||||
<th className="text-right pb-2 px-2">Total</th>
|
<th className="text-right pb-2 px-2">Total</th>
|
||||||
|
|
@ -394,11 +397,10 @@ function BillingPage() {
|
||||||
<td className="py-2 px-2">
|
<td className="py-2 px-2">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={item.strips}
|
value={item.packs}
|
||||||
onChange={(e) => updateItem(idx, "strips", Number(e.target.value))}
|
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"
|
className="w-full px-2 py-1.5 border border-slate-200 rounded text-xs text-center"
|
||||||
min={0}
|
min={0}
|
||||||
disabled={!products?.find((p) => p.id === item.product_id)?.units_per_strip}
|
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 px-2">
|
<td className="py-2 px-2">
|
||||||
|
|
@ -408,6 +410,7 @@ function BillingPage() {
|
||||||
onChange={(e) => updateItem(idx, "loose", Number(e.target.value))}
|
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"
|
className="w-full px-2 py-1.5 border border-slate-200 rounded text-xs text-center"
|
||||||
min={0}
|
min={0}
|
||||||
|
disabled={!products?.find((p) => p.id === item.product_id)?.loose_sale_allowed}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 px-2">
|
<td className="py-2 px-2">
|
||||||
|
|
@ -443,14 +446,6 @@ function BillingPage() {
|
||||||
Bill Summary
|
Bill Summary
|
||||||
</h3>
|
</h3>
|
||||||
<div className="max-w-[360px] ml-auto">
|
<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">
|
<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>Total</span>
|
||||||
<span className="text-blue-600">₹{total.toFixed(2)}</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="Brand Name" value={product.brand} />
|
||||||
<DetailRow label="Category" value={product.category} />
|
<DetailRow label="Category" value={product.category} />
|
||||||
{product.units_per_strip && (
|
{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>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -84,6 +84,7 @@ function ProductDetailsPage() {
|
||||||
<span className="text-xs font-semibold text-slate-600 uppercase tracking-wider">Inventory</span>
|
<span className="text-xs font-semibold text-slate-600 uppercase tracking-wider">Inventory</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-5">
|
<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="Current Stock" value={`${product.size} units`} valueClass={stockClass} />
|
||||||
<DetailRow label="Reorder Level" value={`${product.reorder_level} units`} last />
|
<DetailRow label="Reorder Level" value={`${product.reorder_level} units`} last />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@ const formSchema = CreateProductInput.extend({
|
||||||
selling_price: z.coerce.number().min(0, "Must be ≥ 0"),
|
selling_price: z.coerce.number().min(0, "Must be ≥ 0"),
|
||||||
size: z.coerce.number().int().default(0),
|
size: z.coerce.number().int().default(0),
|
||||||
reorder_level: 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>;
|
type FormValues = z.infer<typeof formSchema>;
|
||||||
|
|
@ -87,7 +88,8 @@ function AddProductPage() {
|
||||||
selling_price: 0,
|
selling_price: 0,
|
||||||
size: 0,
|
size: 0,
|
||||||
reorder_level: 0,
|
reorder_level: 0,
|
||||||
units_per_strip: null,
|
units_per_pack: null,
|
||||||
|
loose_sale_allowed: false,
|
||||||
hide_product_from_public: false,
|
hide_product_from_public: false,
|
||||||
hide_price_from_public: false,
|
hide_price_from_public: false,
|
||||||
compositions: [{ drug_name: "", quantity: "", unit_name: "" }],
|
compositions: [{ drug_name: "", quantity: "", unit_name: "" }],
|
||||||
|
|
@ -99,9 +101,15 @@ function AddProductPage() {
|
||||||
name: "compositions",
|
name: "compositions",
|
||||||
});
|
});
|
||||||
|
|
||||||
const category = watch("category");
|
|
||||||
const hideProduct = watch("hide_product_from_public");
|
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(() => {
|
useEffect(() => {
|
||||||
if (hideProduct) {
|
if (hideProduct) {
|
||||||
|
|
@ -122,7 +130,8 @@ function AddProductPage() {
|
||||||
selling_price: existingProduct.selling_price,
|
selling_price: existingProduct.selling_price,
|
||||||
size: existingProduct.size,
|
size: existingProduct.size,
|
||||||
reorder_level: existingProduct.reorder_level,
|
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_product_from_public: existingProduct.hide_product_from_public,
|
||||||
hide_price_from_public: existingProduct.hide_price_from_public,
|
hide_price_from_public: existingProduct.hide_price_from_public,
|
||||||
compositions: existingProduct.compositions.map((c) => ({
|
compositions: existingProduct.compositions.map((c) => ({
|
||||||
|
|
@ -212,12 +221,19 @@ function AddProductPage() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showUnitsPerStrip && (
|
<div className="col-span-full">
|
||||||
|
<Checkbox
|
||||||
|
{...register("loose_sale_allowed")}
|
||||||
|
label="Loose Sale Allowed"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{showUnitsPerPack && (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-slate-900 mb-1.5">
|
<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>
|
</label>
|
||||||
<Input type="number" {...register("units_per_strip")} placeholder="e.g. 10" />
|
<Input type="number" {...register("units_per_pack")} placeholder="e.g. 10" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -240,7 +256,7 @@ function AddProductPage() {
|
||||||
{errors.unit_name && <p className="text-sm text-red-600 mt-1">{errors.unit_name.message}</p>}
|
{errors.unit_name && <p className="text-sm text-red-600 mt-1">{errors.unit_name.message}</p>}
|
||||||
</div>
|
</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>
|
<label className="block text-sm font-medium text-slate-900 mb-1.5">Distributor</label>
|
||||||
<Select
|
<Select
|
||||||
{...register("distributor_id")}
|
{...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,
|
"when": 1779561725701,
|
||||||
"tag": "0007_yellow_venom",
|
"tag": "0007_yellow_venom",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 8,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1779616862450,
|
||||||
|
"tag": "0008_calm_mimic",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ export type BillItem = {
|
||||||
product_name: string
|
product_name: string
|
||||||
brand: string | null
|
brand: string | null
|
||||||
batch_id: number | null
|
batch_id: number | null
|
||||||
strips: number
|
packs: number
|
||||||
loose: number
|
loose: number
|
||||||
qty: number
|
qty: number
|
||||||
original_price: number
|
original_price: number
|
||||||
|
|
@ -55,7 +55,7 @@ export type BillsRepo = {
|
||||||
product_name: string
|
product_name: string
|
||||||
brand?: string | null
|
brand?: string | null
|
||||||
batch_id?: number | null
|
batch_id?: number | null
|
||||||
strips: number
|
packs: number
|
||||||
loose: number
|
loose: number
|
||||||
qty: number
|
qty: number
|
||||||
original_price: number
|
original_price: number
|
||||||
|
|
@ -90,7 +90,7 @@ function getItemsForBill(billId: number): BillItem[] {
|
||||||
product_name: r.productName,
|
product_name: r.productName,
|
||||||
brand: r.brand,
|
brand: r.brand,
|
||||||
batch_id: r.batchId,
|
batch_id: r.batchId,
|
||||||
strips: r.strips,
|
packs: r.packs,
|
||||||
loose: r.loose,
|
loose: r.loose,
|
||||||
qty: r.qty,
|
qty: r.qty,
|
||||||
original_price: r.originalPrice,
|
original_price: r.originalPrice,
|
||||||
|
|
@ -147,7 +147,7 @@ export function createBillsRepo(): { repo: BillsRepo } {
|
||||||
productName: item.product_name,
|
productName: item.product_name,
|
||||||
brand: item.brand ?? null,
|
brand: item.brand ?? null,
|
||||||
batchId: item.batch_id ?? null,
|
batchId: item.batch_id ?? null,
|
||||||
strips: item.strips,
|
packs: item.packs,
|
||||||
loose: item.loose,
|
loose: item.loose,
|
||||||
qty: item.qty,
|
qty: item.qty,
|
||||||
originalPrice: item.original_price,
|
originalPrice: item.original_price,
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,8 @@ interface ProductFields {
|
||||||
selling_price: number
|
selling_price: number
|
||||||
size: number
|
size: number
|
||||||
reorder_level: number
|
reorder_level: number
|
||||||
units_per_strip: number | null
|
units_per_pack: number | null
|
||||||
|
loose_sale_allowed: boolean
|
||||||
hide_product_from_public: boolean
|
hide_product_from_public: boolean
|
||||||
hide_price_from_public: boolean
|
hide_price_from_public: boolean
|
||||||
}
|
}
|
||||||
|
|
@ -41,7 +42,7 @@ export type Product = ProductFields & {
|
||||||
compositions: CompositionItem[]
|
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>
|
type ProductRequiredKeys = Exclude<keyof ProductFields, ProductOptionalKeys>
|
||||||
|
|
||||||
export type CreateProductInput =
|
export type CreateProductInput =
|
||||||
|
|
@ -93,7 +94,8 @@ function toProduct(
|
||||||
selling_price: row.sellingPrice,
|
selling_price: row.sellingPrice,
|
||||||
size: row.size,
|
size: row.size,
|
||||||
reorder_level: row.reorderLevel,
|
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_product_from_public: row.hideProductFromPublic,
|
||||||
hide_price_from_public: row.hidePriceFromPublic,
|
hide_price_from_public: row.hidePriceFromPublic,
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +194,8 @@ export function createProductsRepo(): { repo: ProductsRepo } {
|
||||||
sellingPrice: input.selling_price,
|
sellingPrice: input.selling_price,
|
||||||
size: input.size,
|
size: input.size,
|
||||||
reorderLevel: input.reorder_level,
|
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,
|
hideProductFromPublic: input.hide_product_from_public ?? false,
|
||||||
hidePriceFromPublic: input.hide_price_from_public ?? false,
|
hidePriceFromPublic: input.hide_price_from_public ?? false,
|
||||||
enterpriseId,
|
enterpriseId,
|
||||||
|
|
@ -231,7 +234,8 @@ export function createProductsRepo(): { repo: ProductsRepo } {
|
||||||
if (patch.selling_price !== undefined) setData.sellingPrice = patch.selling_price
|
if (patch.selling_price !== undefined) setData.sellingPrice = patch.selling_price
|
||||||
if (patch.size !== undefined) setData.size = patch.size
|
if (patch.size !== undefined) setData.size = patch.size
|
||||||
if (patch.reorder_level !== undefined) setData.reorderLevel = patch.reorder_level
|
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_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
|
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),
|
batchId: integer('batch_id').references(() => stockBatches.id),
|
||||||
productName: text('product_name').notNull(),
|
productName: text('product_name').notNull(),
|
||||||
brand: text('brand'),
|
brand: text('brand'),
|
||||||
strips: integer('strips').notNull().default(0),
|
packs: integer('packs').notNull().default(0),
|
||||||
loose: integer('loose').notNull().default(0),
|
loose: integer('loose').notNull().default(0),
|
||||||
qty: integer('qty').notNull(),
|
qty: integer('qty').notNull(),
|
||||||
originalPrice: real('original_price').notNull(),
|
originalPrice: real('original_price').notNull(),
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ export const products = sqliteTable('products', {
|
||||||
sellingPrice: real('selling_price').notNull(),
|
sellingPrice: real('selling_price').notNull(),
|
||||||
size: integer('size').notNull().default(0),
|
size: integer('size').notNull().default(0),
|
||||||
reorderLevel: integer('reorder_level').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),
|
hideProductFromPublic: integer('hide_product_from_public', { mode: 'boolean' }).notNull().default(false),
|
||||||
hidePriceFromPublic: integer('hide_price_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),
|
enterpriseId: integer('enterprise_id').references(() => enterprises.id),
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,8 @@ export const ProductSchema = z.object({
|
||||||
selling_price: z.number(),
|
selling_price: z.number(),
|
||||||
size: z.number().int(),
|
size: z.number().int(),
|
||||||
reorder_level: 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_product_from_public: z.boolean(),
|
||||||
hide_price_from_public: z.boolean(),
|
hide_price_from_public: z.boolean(),
|
||||||
compositions: z.array(CompositionItemSchema),
|
compositions: z.array(CompositionItemSchema),
|
||||||
|
|
@ -44,7 +45,8 @@ export const CreateProductInput = z.object({
|
||||||
selling_price: shape.selling_price.min(0),
|
selling_price: shape.selling_price.min(0),
|
||||||
size: shape.size.default(0),
|
size: shape.size.default(0),
|
||||||
reorder_level: shape.reorder_level.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_product_from_public: shape.hide_product_from_public.default(false),
|
||||||
hide_price_from_public: shape.hide_price_from_public.default(false),
|
hide_price_from_public: shape.hide_price_from_public.default(false),
|
||||||
compositions: z.array(CompositionInputSchema).default([]),
|
compositions: z.array(CompositionInputSchema).default([]),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue