feat(interests): CM-2 Part B — interest_berths price override (data + resolver)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-19 10:40:17 +02:00
parent b3753b96a1
commit 039ef25fe5
4 changed files with 91 additions and 0 deletions

View File

@@ -170,6 +170,8 @@ export async function listBerthsForInterest(
addedBy: interestBerths.addedBy,
addedAt: interestBerths.addedAt,
notes: interestBerths.notes,
priceOverride: interestBerths.priceOverride,
priceOverrideCurrency: interestBerths.priceOverrideCurrency,
mooringNumber: berths.mooringNumber,
area: berths.area,
status: berths.status,
@@ -444,3 +446,49 @@ export async function removeInterestBerth(
.delete(interestBerths)
.where(and(eq(interestBerths.interestId, interestId), eq(interestBerths.berthId, berthId)));
}
// ─── Per-interest price override (CM-2 Part B) ───────────────────────────────
/**
* Resolve the effective price for a berth in the context of an interest. The
* deal-specific override (when set) supersedes the berth's canonical list
* price; the override carries its own currency, falling back to the base
* currency when null. Pure — safe to unit-test without a DB.
*/
export function resolveBerthPriceForInterest(
override: { priceOverride: string | null; priceOverrideCurrency: string | null },
base: { price: string | null; priceCurrency: string },
): { price: string | null; currency: string } {
if (override.priceOverride != null) {
return {
price: override.priceOverride,
currency: override.priceOverrideCurrency ?? base.priceCurrency,
};
}
return { price: base.price, currency: base.priceCurrency };
}
/**
* Set (or clear, when `price` is null) the deal-specific price for one
* (interest, berth). Tenant-scoped: the interest must belong to `portId`.
* Does not touch `berths.price`.
*/
export async function setBerthPriceOverride(
interestId: string,
berthId: string,
price: number | null,
currency: string | null,
portId: string,
): Promise<void> {
const interestRow = await db.query.interests.findFirst({
where: and(eq(interests.id, interestId), eq(interests.portId, portId)),
});
if (!interestRow) throw new NotFoundError('Interest');
await db
.update(interestBerths)
.set({
priceOverride: price == null ? null : String(price),
priceOverrideCurrency: price == null ? null : (currency ?? 'USD'),
})
.where(and(eq(interestBerths.interestId, interestId), eq(interestBerths.berthId, berthId)));
}