Auto-fill AssetItem.warrantyEndDate from AssetModel.warrantyMonths
CI / test (push) Failing after 8s
CI / test (push) Failing after 8s
AssetModel gains an optional warrantyMonths (standard vendor warranty length). createAssetItem() now defaults warrantyEndDate to purchaseDate + warrantyMonths (month-end clamped, e.g. Dec 31 + 2mo -> Feb 28, not Mar 3) whenever the caller omits warrantyEndDate -- still overridable per item for batches with extended/different warranty terms. /asset-models UI gets an input for the new field; /assets needs no change since it already omits warrantyEndDate from the payload when left blank. Hand-written migration + manual smoke test (tsx script, not Vitest), same deviation as the other 2026-08-2x work in this branch -- see CLAUDE.md. tsc/lint clean.
This commit is contained in:
+38
-1
@@ -136,16 +136,53 @@ export type CreateAssetItemResult =
|
||||
* createPurchaseOrder() pattern for disambiguating between two FKs on the
|
||||
* same model.
|
||||
*/
|
||||
// Adds `months` calendar months to `date`, clamping to the last day of the
|
||||
// target month rather than letting JS's native Date rollover push into the
|
||||
// month after (e.g. Jan 31 + 1 month must land on Feb 28/29, not Mar
|
||||
// 2/3 -- `date.setMonth()` alone does the latter). UTC throughout: Prisma
|
||||
// @db.Date fields round-trip as UTC-midnight Dates (see dateString()
|
||||
// above), and doing this in UTC avoids the host server's local timezone
|
||||
// shifting the day near midnight.
|
||||
function addMonthsClamped(date: Date, months: number): Date {
|
||||
const targetMonthIndex = date.getUTCMonth() + months;
|
||||
const daysInTargetMonth = new Date(
|
||||
Date.UTC(date.getUTCFullYear(), targetMonthIndex + 1, 0)
|
||||
).getUTCDate();
|
||||
return new Date(
|
||||
Date.UTC(
|
||||
date.getUTCFullYear(),
|
||||
targetMonthIndex,
|
||||
Math.min(date.getUTCDate(), daysInTargetMonth)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export async function createAssetItem(
|
||||
input: CreateAssetItemInput
|
||||
): Promise<CreateAssetItemResult> {
|
||||
try {
|
||||
// warrantyEndDate defaults from the model's standard warranty term
|
||||
// (AssetModel.warrantyMonths) when the caller doesn't supply one
|
||||
// explicitly -- see schema.prisma's AssetModel.warrantyMonths comment
|
||||
// for why this stays a suggestion (still overridable per item) rather
|
||||
// than a hard-computed column. A modelId that doesn't exist is left for
|
||||
// the insert below's own P2003 handling, same as before this lookup was
|
||||
// added -- this just also opportunistically uses the model row if it's
|
||||
// there.
|
||||
let warrantyEndDate = input.warrantyEndDate ?? null;
|
||||
if (!warrantyEndDate) {
|
||||
const model = await prisma.assetModel.findUnique({ where: { id: input.modelId } });
|
||||
if (model?.warrantyMonths) {
|
||||
warrantyEndDate = addMonthsClamped(input.purchaseDate, model.warrantyMonths);
|
||||
}
|
||||
}
|
||||
|
||||
const created = await prisma.assetItem.create({
|
||||
data: {
|
||||
serialNumber: input.serialNumber,
|
||||
modelId: input.modelId,
|
||||
purchaseDate: input.purchaseDate,
|
||||
warrantyEndDate: input.warrantyEndDate ?? null,
|
||||
warrantyEndDate,
|
||||
purchaseOrderId: input.purchaseOrderId ?? null,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user