feat(tenancies-p4): public-map status flip via active permanent tenancy
- derivePublicStatus gains optional hasActivePermanentTenancy flag; precedence updated to "sold > under_offer > available" where Sold can come from EITHER berths.status='sold' (admin set) OR an active permanent-class tenancy (only when module enabled). - Permanent-class tenure types defined in one place (isPermanentTenureType): permanent | fee_simple | strata_lot. Seasonal / fixed_term tenancies do NOT flip — they fall through to the existing under_offer / available precedence. - /api/public/berths (list) + /api/public/berths/[mooringNumber] (single) both gate the lookup on isTenanciesModuleEnabled(portId). Disabled module = lookup skipped entirely, preserving pre-module behaviour for ports that haven't opted in. - 8 new unit tests covering: flip from available, flip from under_offer, explicit sold idempotency, false-flag fallthrough, default-omit pre- module behaviour, permanent-class membership for each tenure type, and null/undefined/unknown rejection. Verified: tsc clean, 1493/1493 vitest. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,9 +5,11 @@ import { db } from '@/lib/db';
|
||||
import { ports } from '@/lib/db/schema/ports';
|
||||
import { berths, berthMapData } from '@/lib/db/schema/berths';
|
||||
import { interestBerths, interests } from '@/lib/db/schema/interests';
|
||||
import { berthTenancies } from '@/lib/db/schema/tenancies';
|
||||
import { errorResponse } from '@/lib/errors';
|
||||
import { logger } from '@/lib/logger';
|
||||
import { toPublicBerth } from '@/lib/services/public-berths';
|
||||
import { isTenanciesModuleEnabled } from '@/lib/services/tenancies-module.service';
|
||||
import { isPermanentTenureType, toPublicBerth } from '@/lib/services/public-berths';
|
||||
|
||||
/**
|
||||
* GET /api/public/berths/[mooringNumber]
|
||||
@@ -105,7 +107,32 @@ export async function GET(
|
||||
.limit(1),
|
||||
]);
|
||||
|
||||
const out = toPublicBerth(berth, mapData[0] ?? null, specificInterestRows.length > 0);
|
||||
// Tenancies-module status flip: an active permanent-class tenancy
|
||||
// pushes the berth to "Sold" when the module is enabled for this port.
|
||||
let hasActivePermanentTenancy = false;
|
||||
if (await isTenanciesModuleEnabled(port.id)) {
|
||||
const activeTenancy = await db
|
||||
.select({ tenureType: berthTenancies.tenureType })
|
||||
.from(berthTenancies)
|
||||
.where(
|
||||
and(
|
||||
eq(berthTenancies.portId, port.id),
|
||||
eq(berthTenancies.berthId, berth.id),
|
||||
eq(berthTenancies.status, 'active'),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
hasActivePermanentTenancy = activeTenancy.some((row) =>
|
||||
isPermanentTenureType(row.tenureType),
|
||||
);
|
||||
}
|
||||
|
||||
const out = toPublicBerth(
|
||||
berth,
|
||||
mapData[0] ?? null,
|
||||
specificInterestRows.length > 0,
|
||||
hasActivePermanentTenancy,
|
||||
);
|
||||
|
||||
if (out.Status !== 'Available' && out.Status !== 'Under Offer' && out.Status !== 'Sold') {
|
||||
logger.error({ berthId: berth.id, status: out.Status }, 'Public berth status out of range');
|
||||
|
||||
Reference in New Issue
Block a user