diff --git a/src/lib/services/eoi-context.ts b/src/lib/services/eoi-context.ts index 0346795a..af4479ae 100644 --- a/src/lib/services/eoi-context.ts +++ b/src/lib/services/eoi-context.ts @@ -303,6 +303,28 @@ export async function buildEoiContext(interestId: string, portId: string): Promi const today = now.toISOString().slice(0, 10); const year = String(now.getFullYear()); + // CM-2 Part B: deal-specific price override. The base berth price is the + // canonical list price; an interest_berths.price_override (when set) + // supersedes it for THIS interest's documents via the existing + // {{berth.price}} / {{berth.priceCurrency}} tokens. + let resolvedBerthPrice = berth?.price ?? null; + let resolvedBerthCurrency = berth?.priceCurrency ?? port.defaultCurrency; + if (berth && primaryBerthId) { + const [ibOverride] = await db + .select({ + priceOverride: interestBerths.priceOverride, + priceOverrideCurrency: interestBerths.priceOverrideCurrency, + }) + .from(interestBerths) + .where( + and(eq(interestBerths.interestId, interest.id), eq(interestBerths.berthId, primaryBerthId)), + ); + if (ibOverride?.priceOverride != null) { + resolvedBerthPrice = ibOverride.priceOverride; + resolvedBerthCurrency = ibOverride.priceOverrideCurrency ?? berth.priceCurrency; + } + } + return { client: { id: client.id, @@ -337,8 +359,8 @@ export async function buildEoiContext(interestId: string, portId: string): Promi mooringNumber: berth.mooringNumber, area: berth.area, lengthFt: berth.lengthFt, - price: berth.price, - priceCurrency: berth.priceCurrency, + price: resolvedBerthPrice, + priceCurrency: resolvedBerthCurrency, tenureType: berth.tenureType, } : null, diff --git a/tests/unit/services/eoi-context.test.ts b/tests/unit/services/eoi-context.test.ts index 6e1da174..43bad46d 100644 --- a/tests/unit/services/eoi-context.test.ts +++ b/tests/unit/services/eoi-context.test.ts @@ -1,6 +1,7 @@ import { describe, it, expect } from 'vitest'; import { buildEoiContext } from '@/lib/services/eoi-context'; +import { setBerthPriceOverride } from '@/lib/services/interest-berths.service'; import { makePort, makeClient, makeCompany, makeBerth, makeYacht } from '../../helpers/factories'; import { db } from '@/lib/db'; import { @@ -338,6 +339,32 @@ describe('buildEoiContext', () => { await expect(buildEoiContext(interest.id, port.id)).rejects.toThrow(/client address/i); }); + it('renders the deal-specific override price for the primary berth', async () => { + const port = await makePort(); + const client = await makeClient({ portId: port.id }); + await seedClientEoiPrereqs({ clientId: client.id, portId: port.id }); + const berth = await makeBerth({ + portId: port.id, + overrides: { mooringNumber: 'P-1', price: '3880800', priceCurrency: 'USD' }, + }); + const interest = await insertInterest({ + portId: port.id, + clientId: client.id, + berthId: berth.id, + }); + + // Base list price flows through first. + let ctx = await buildEoiContext(interest.id, port.id); + expect(ctx.berth?.price).toBe('3880800'); + expect(ctx.berth?.priceCurrency).toBe('USD'); + + // The deal-specific override supersedes the list price for this interest. + await setBerthPriceOverride(interest.id, berth.id, 1500000, 'EUR', port.id); + ctx = await buildEoiContext(interest.id, port.id); + expect(ctx.berth?.price).toBe('1500000'); + expect(ctx.berth?.priceCurrency).toBe('EUR'); + }); + it('throws NotFoundError for non-existent interest', async () => { const port = await makePort(); await expect(buildEoiContext('fake-id', port.id)).rejects.toThrow(NotFoundError);