feat(interests): CM-2 Part B — EOI/doc generation honours berth price override
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -303,6 +303,28 @@ export async function buildEoiContext(interestId: string, portId: string): Promi
|
|||||||
const today = now.toISOString().slice(0, 10);
|
const today = now.toISOString().slice(0, 10);
|
||||||
const year = String(now.getFullYear());
|
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 {
|
return {
|
||||||
client: {
|
client: {
|
||||||
id: client.id,
|
id: client.id,
|
||||||
@@ -337,8 +359,8 @@ export async function buildEoiContext(interestId: string, portId: string): Promi
|
|||||||
mooringNumber: berth.mooringNumber,
|
mooringNumber: berth.mooringNumber,
|
||||||
area: berth.area,
|
area: berth.area,
|
||||||
lengthFt: berth.lengthFt,
|
lengthFt: berth.lengthFt,
|
||||||
price: berth.price,
|
price: resolvedBerthPrice,
|
||||||
priceCurrency: berth.priceCurrency,
|
priceCurrency: resolvedBerthCurrency,
|
||||||
tenureType: berth.tenureType,
|
tenureType: berth.tenureType,
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
|
|
||||||
import { buildEoiContext } from '@/lib/services/eoi-context';
|
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 { makePort, makeClient, makeCompany, makeBerth, makeYacht } from '../../helpers/factories';
|
||||||
import { db } from '@/lib/db';
|
import { db } from '@/lib/db';
|
||||||
import {
|
import {
|
||||||
@@ -338,6 +339,32 @@ describe('buildEoiContext', () => {
|
|||||||
await expect(buildEoiContext(interest.id, port.id)).rejects.toThrow(/client address/i);
|
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 () => {
|
it('throws NotFoundError for non-existent interest', async () => {
|
||||||
const port = await makePort();
|
const port = await makePort();
|
||||||
await expect(buildEoiContext('fake-id', port.id)).rejects.toThrow(NotFoundError);
|
await expect(buildEoiContext('fake-id', port.id)).rejects.toThrow(NotFoundError);
|
||||||
|
|||||||
Reference in New Issue
Block a user