cost and price
This commit is contained in:
@@ -156,6 +156,10 @@ export function InventoryDetailPage() {
|
||||
<dt className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Default cost</dt>
|
||||
<dd className="mt-2 text-sm text-text">{item.defaultCost == null ? "Not set" : `$${item.defaultCost.toFixed(2)}`}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Default price</dt>
|
||||
<dd className="mt-2 text-sm text-text">{item.defaultPrice == null ? "Not set" : `$${item.defaultPrice.toFixed(2)}`}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Flags</dt>
|
||||
<dd className="mt-2 text-sm text-text">
|
||||
|
||||
@@ -71,6 +71,7 @@ export function InventoryFormPage({ mode }: InventoryFormPageProps) {
|
||||
isSellable: item.isSellable,
|
||||
isPurchasable: item.isPurchasable,
|
||||
defaultCost: item.defaultCost,
|
||||
defaultPrice: item.defaultPrice,
|
||||
notes: item.notes,
|
||||
bomLines: item.bomLines.map((line) => ({
|
||||
componentItemId: line.componentItemId,
|
||||
@@ -199,6 +200,17 @@ export function InventoryFormPage({ mode }: InventoryFormPageProps) {
|
||||
className="w-full rounded-2xl border border-line/70 bg-page px-2 py-2 text-text outline-none transition focus:border-brand"
|
||||
/>
|
||||
</label>
|
||||
<label className="block">
|
||||
<span className="mb-2 block text-sm font-semibold text-text">Default price</span>
|
||||
<input
|
||||
type="number"
|
||||
min={0}
|
||||
step={0.01}
|
||||
value={form.defaultPrice ?? ""}
|
||||
onChange={(event) => updateField("defaultPrice", event.target.value === "" ? null : Number(event.target.value))}
|
||||
className="w-full rounded-2xl border border-line/70 bg-page px-2 py-2 text-text outline-none transition focus:border-brand"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className="grid gap-3 xl:grid-cols-4">
|
||||
<label className="block">
|
||||
|
||||
@@ -32,6 +32,7 @@ export const emptyInventoryItemInput: InventoryItemInput = {
|
||||
isSellable: true,
|
||||
isPurchasable: true,
|
||||
defaultCost: null,
|
||||
defaultPrice: null,
|
||||
notes: "",
|
||||
bomLines: [],
|
||||
};
|
||||
|
||||
@@ -321,6 +321,7 @@ export function SalesFormPage({ entity, mode }: { entity: SalesDocumentEntity; m
|
||||
...line,
|
||||
itemId: option.id,
|
||||
description: line.description || option.name,
|
||||
unitPrice: line.unitPrice > 0 ? line.unitPrice : (option.defaultPrice ?? 0),
|
||||
});
|
||||
updateLineSearchTerm(index, option.sku);
|
||||
setActiveLinePicker(null);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "InventoryItem" ADD COLUMN "defaultPrice" REAL;
|
||||
@@ -113,6 +113,7 @@ model InventoryItem {
|
||||
isSellable Boolean @default(true)
|
||||
isPurchasable Boolean @default(true)
|
||||
defaultCost Float?
|
||||
defaultPrice Float?
|
||||
notes String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@ -176,6 +176,7 @@ export async function bootstrapAppData() {
|
||||
isSellable: false,
|
||||
isPurchasable: true,
|
||||
defaultCost: 42.5,
|
||||
defaultPrice: null,
|
||||
notes: "Primary sheet stock for enclosure fabrication.",
|
||||
},
|
||||
});
|
||||
@@ -191,6 +192,7 @@ export async function bootstrapAppData() {
|
||||
isSellable: false,
|
||||
isPurchasable: true,
|
||||
defaultCost: 0.18,
|
||||
defaultPrice: null,
|
||||
notes: "Bulk hardware item.",
|
||||
},
|
||||
});
|
||||
@@ -206,6 +208,7 @@ export async function bootstrapAppData() {
|
||||
isSellable: true,
|
||||
isPurchasable: false,
|
||||
defaultCost: 118,
|
||||
defaultPrice: 249,
|
||||
notes: "Starter BOM for the inventory foundation slice.",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -37,6 +37,7 @@ const inventoryItemSchema = z.object({
|
||||
isSellable: z.boolean(),
|
||||
isPurchasable: z.boolean(),
|
||||
defaultCost: z.number().nonnegative().nullable(),
|
||||
defaultPrice: z.number().nonnegative().nullable(),
|
||||
notes: z.string(),
|
||||
bomLines: z.array(bomLineSchema),
|
||||
});
|
||||
|
||||
@@ -45,6 +45,7 @@ type InventoryDetailRecord = {
|
||||
isSellable: boolean;
|
||||
isPurchasable: boolean;
|
||||
defaultCost: number | null;
|
||||
defaultPrice: number | null;
|
||||
notes: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
@@ -220,6 +221,7 @@ function mapDetail(record: InventoryDetailRecord): InventoryItemDetailDto {
|
||||
}),
|
||||
description: record.description,
|
||||
defaultCost: record.defaultCost,
|
||||
defaultPrice: record.defaultPrice,
|
||||
notes: record.notes,
|
||||
createdAt: record.createdAt.toISOString(),
|
||||
bomLines: record.bomLines.slice().sort((a, b) => a.position - b.position).map(mapBomLine),
|
||||
@@ -371,6 +373,7 @@ export async function listInventoryItemOptions() {
|
||||
id: true,
|
||||
sku: true,
|
||||
name: true,
|
||||
defaultPrice: true,
|
||||
},
|
||||
orderBy: [{ sku: "asc" }],
|
||||
});
|
||||
@@ -379,6 +382,7 @@ export async function listInventoryItemOptions() {
|
||||
id: item.id,
|
||||
sku: item.sku,
|
||||
name: item.name,
|
||||
defaultPrice: item.defaultPrice,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -517,6 +521,7 @@ export async function createInventoryItem(payload: InventoryItemInput) {
|
||||
isSellable: payload.isSellable,
|
||||
isPurchasable: payload.isPurchasable,
|
||||
defaultCost: payload.defaultCost,
|
||||
defaultPrice: payload.defaultPrice,
|
||||
notes: payload.notes,
|
||||
bomLines: validatedBom.bomLines.length
|
||||
? {
|
||||
@@ -558,6 +563,7 @@ export async function updateInventoryItem(itemId: string, payload: InventoryItem
|
||||
isSellable: payload.isSellable,
|
||||
isPurchasable: payload.isPurchasable,
|
||||
defaultCost: payload.defaultCost,
|
||||
defaultPrice: payload.defaultPrice,
|
||||
notes: payload.notes,
|
||||
bomLines: {
|
||||
deleteMany: {},
|
||||
|
||||
@@ -31,6 +31,7 @@ export interface InventoryItemOptionDto {
|
||||
id: string;
|
||||
sku: string;
|
||||
name: string;
|
||||
defaultPrice: number | null;
|
||||
}
|
||||
|
||||
export interface WarehouseLocationOptionDto {
|
||||
@@ -128,6 +129,7 @@ export interface InventoryTransactionInput {
|
||||
export interface InventoryItemDetailDto extends InventoryItemSummaryDto {
|
||||
description: string;
|
||||
defaultCost: number | null;
|
||||
defaultPrice: number | null;
|
||||
notes: string;
|
||||
createdAt: string;
|
||||
bomLines: InventoryBomLineDto[];
|
||||
@@ -146,6 +148,7 @@ export interface InventoryItemInput {
|
||||
isSellable: boolean;
|
||||
isPurchasable: boolean;
|
||||
defaultCost: number | null;
|
||||
defaultPrice: number | null;
|
||||
notes: string;
|
||||
bomLines: InventoryBomLineInput[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user