diff --git a/docs/runbooks/permission-audit.md b/docs/runbooks/permission-audit.md
index 5e29c75d..31914786 100644
--- a/docs/runbooks/permission-audit.md
+++ b/docs/runbooks/permission-audit.md
@@ -28,7 +28,6 @@ Scanned 182 route files under `src/app/api/v1/`.
| `src/app/api/v1/alerts/[id]/dismiss/route.ts` | POST | Alerts are user-scoped; port-filtered via auth context. |
| `src/app/api/v1/alerts/count/route.ts` | GET | Alerts are user-scoped; port-filtered via auth context. |
| `src/app/api/v1/alerts/route.ts` | GET | Alerts are user-scoped; port-filtered via auth context. |
-| `src/app/api/v1/berth-reservations/[id]/route.ts` | PATCH | TODO: PATCH should map to reservations:edit (not currently in catalog). |
| `src/app/api/v1/currency/convert/route.ts` | POST | Currency reference data; port-scoped, no PII. |
| `src/app/api/v1/currency/rates/refresh/route.ts` | POST | TODO: gate with admin:manage_settings — currently allow-listed. |
| `src/app/api/v1/currency/rates/route.ts` | GET | Currency reference data; port-scoped, no PII. |
diff --git a/docs/tenancies-design.md b/docs/tenancies-design.md
index 4a2719c8..0d561cce 100644
--- a/docs/tenancies-design.md
+++ b/docs/tenancies-design.md
@@ -237,7 +237,7 @@ The `tenancies_module` integration check resolves to `tenancies_module_enabled =
## Service layer additions
-`src/lib/services/tenancies.service.ts` (renamed from `berth-reservations.service.ts`):
+`src/lib/services/berth-tenancies.service.ts` (renamed from `berth-reservations.service.ts`):
- `listTenancies({ portId, filters, page })` — gated read.
- `createTenancy(portId, data, meta)` — mints a row; also triggers the module-enable flip on first insert.
diff --git a/scripts/audit-permissions.ts b/scripts/audit-permissions.ts
index d1ca64e3..0431c041 100644
--- a/scripts/audit-permissions.ts
+++ b/scripts/audit-permissions.ts
@@ -73,10 +73,6 @@ const ALLOW_LIST: ReadonlyArray<{ pattern: RegExp; reason: string }> = [
pattern: /\/custom-fields\/\[entityId\]\//,
reason: 'TODO: needs custom_fields:* permission. PUT path internally validated.',
},
- {
- pattern: /\/berth-reservations\/\[id\]\/route\.ts$/,
- reason: 'TODO: PATCH should map to reservations:edit (not currently in catalog).',
- },
];
interface Finding {
diff --git a/src/app/(dashboard)/[portSlug]/admin/import/page.tsx b/src/app/(dashboard)/[portSlug]/admin/import/page.tsx
index 592344df..8a3318df 100644
--- a/src/app/(dashboard)/[portSlug]/admin/import/page.tsx
+++ b/src/app/(dashboard)/[portSlug]/admin/import/page.tsx
@@ -62,7 +62,7 @@ export default function DataImportPage() {
Dry-run preview that shows new vs. matched-existing rows before commit.
Conflict-resolution choices (skip, update, dedup-by-email) per import type.
Per-port import history with rollback.
- Templates for clients, yachts, companies, berths, reservations, expenses.
+ Templates for clients, yachts, companies, berths, tenancies, expenses.
Imports run against the BullMQ import queue (concurrency 1) so partial
diff --git a/src/app/api/v1/admin/users/[id]/permission-overrides/route.ts b/src/app/api/v1/admin/users/[id]/permission-overrides/route.ts
index 374f05cc..0e5915c7 100644
--- a/src/app/api/v1/admin/users/[id]/permission-overrides/route.ts
+++ b/src/app/api/v1/admin/users/[id]/permission-overrides/route.ts
@@ -67,7 +67,7 @@ const ALLOWED_RESOURCE_ACTIONS: Record> = {
yachts: new Set(['view', 'create', 'edit', 'delete', 'transfer']),
companies: new Set(['view', 'create', 'edit', 'delete']),
memberships: new Set(['view', 'manage']),
- reservations: new Set(['view', 'create', 'activate', 'cancel']),
+ tenancies: new Set(['view', 'manage', 'cancel']),
admin: new Set([
'manage_users',
'view_audit_log',
diff --git a/src/components/admin/admin-sections-browser.tsx b/src/components/admin/admin-sections-browser.tsx
index 7168dbfb..7edcf417 100644
--- a/src/components/admin/admin-sections-browser.tsx
+++ b/src/components/admin/admin-sections-browser.tsx
@@ -189,7 +189,7 @@ const GROUPS: AdminGroup[] = [
{
href: 'custom-fields',
label: 'Custom Fields',
- description: 'Tenant-defined fields for clients, yachts, and reservations.',
+ description: 'Tenant-defined fields for clients, yachts, and tenancies.',
icon: SlidersHorizontal,
},
{
@@ -261,7 +261,7 @@ const GROUPS: AdminGroup[] = [
{
href: 'import',
label: 'Bulk Import',
- description: 'CSV-driven imports for clients, yachts, and reservations.',
+ description: 'CSV-driven imports for clients, yachts, and tenancies.',
icon: FileUp,
},
{
diff --git a/src/components/clients/bulk-archive-wizard.tsx b/src/components/clients/bulk-archive-wizard.tsx
index 54ab030c..e92d5d83 100644
--- a/src/components/clients/bulk-archive-wizard.tsx
+++ b/src/components/clients/bulk-archive-wizard.tsx
@@ -170,7 +170,7 @@ function BulkArchiveWizardBody({ open, onOpenChange, clientIds, onSuccess }: Pro
Low-stakes defaults: release available/under-offer berths, keep sold ones, cancel
- reservations, leave invoices/signing requests alone. Yachts stay on the archived
+ tenancies, leave invoices/signing requests alone. Yachts stay on the archived
client. To customise per-client, archive that client individually instead.
diff --git a/src/components/clients/bulk-hard-delete-dialog.tsx b/src/components/clients/bulk-hard-delete-dialog.tsx
index 9ba1ea89..a88a5d0b 100644
--- a/src/components/clients/bulk-hard-delete-dialog.tsx
+++ b/src/components/clients/bulk-hard-delete-dialog.tsx
@@ -128,8 +128,8 @@ function BulkHardDeleteDialogBody({ onOpenChange, clientIds, onDeleted }: Props)
For each client we delete: client record + addresses, contacts, notes, tags, portal
- user, GDPR records, all interests, all reservations. Signed documents, email threads,
- files and reminders are detached but kept.
+ user, GDPR records, all interests, all tenancies. Signed documents, email threads, files
+ and reminders are detached but kept.
)}
diff --git a/src/components/clients/client-detail.tsx b/src/components/clients/client-detail.tsx
index 38961a5e..ecaa9a9d 100644
--- a/src/components/clients/client-detail.tsx
+++ b/src/components/clients/client-detail.tsx
@@ -114,6 +114,7 @@ export function ClientDetail({ clientId, currentUserId }: ClientDetailProps) {
'yacht:ownership_transferred': [['clients', clientId]],
'company_membership:added': [['clients', clientId]],
'company_membership:ended': [['clients', clientId]],
+ 'berth_tenancy:created': [['clients', clientId]],
'berth_tenancy:activated': [['clients', clientId]],
'berth_tenancy:ended': [['clients', clientId]],
'berth_tenancy:cancelled': [['clients', clientId]],
diff --git a/src/components/clients/gdpr-export-button.tsx b/src/components/clients/gdpr-export-button.tsx
index 57bb965e..fe11ec70 100644
--- a/src/components/clients/gdpr-export-button.tsx
+++ b/src/components/clients/gdpr-export-button.tsx
@@ -120,7 +120,7 @@ export function GdprExportButton({ clientId }: { clientId: string }) {
Personal data export
Bundles every record we hold about this client (profile, contacts, addresses, yachts,
- companies, interests, reservations, invoices, documents, audit log) into a ZIP with JSON
+ companies, interests, tenancies, invoices, documents, audit log) into a ZIP with JSON
and HTML copies. Used to satisfy GDPR Article 15 access requests.
diff --git a/src/components/clients/hard-delete-dialog.tsx b/src/components/clients/hard-delete-dialog.tsx
index aa242b42..f17f4959 100644
--- a/src/components/clients/hard-delete-dialog.tsx
+++ b/src/components/clients/hard-delete-dialog.tsx
@@ -114,7 +114,7 @@ function HardDeleteDialogBody({ onOpenChange, clientId, clientName, onDeleted }:
- Client record + addresses, contacts, notes, tags
- Portal user account + GDPR consent records
- - All pipeline interests + reservations for this client
+ - All pipeline interests + tenancies for this client
What is preserved
diff --git a/src/components/interests/won-status-panel.tsx b/src/components/interests/won-status-panel.tsx
index 8e6124d7..f0f5acdc 100644
--- a/src/components/interests/won-status-panel.tsx
+++ b/src/components/interests/won-status-panel.tsx
@@ -100,9 +100,9 @@ export function WonStatusPanel({ interestId, outcome }: WonStatusPanelProps) {
Won - wrap-up checklist
- Upload anything that didn't flow through the system automatically. Reservations,
- deposit invoicing, and client billing are handled outside the CRM - this checklist is for
- the paperwork that lives on the deal itself.
+ Upload anything that didn't flow through the system automatically. Tenancies, deposit
+ invoicing, and client billing are handled outside the CRM - this checklist is for the
+ paperwork that lives on the deal itself.
diff --git a/src/lib/services/alert-rules.ts b/src/lib/services/alert-rules.ts
index b8f61fd3..03ef4734 100644
--- a/src/lib/services/alert-rules.ts
+++ b/src/lib/services/alert-rules.ts
@@ -65,7 +65,7 @@ async function reservationNoAgreement(portId: string): Promise
title: `Reservation needs an agreement`,
body: `Active reservation for ${r.yachtName} (${r.clientName}) has no signed agreement yet.`,
link: `/[port]/tenancies/${r.id}`,
- entityType: 'reservation',
+ entityType: 'berth_tenancy',
entityId: r.id,
}));
}
diff --git a/src/lib/services/client-merge.service.ts b/src/lib/services/client-merge.service.ts
index 3e460e75..7e060d5c 100644
--- a/src/lib/services/client-merge.service.ts
+++ b/src/lib/services/client-merge.service.ts
@@ -80,7 +80,7 @@ export interface MergeResult {
notes: number;
tags: number;
relationships: number;
- reservations: number;
+ tenancies: number;
};
}
@@ -152,7 +152,7 @@ export async function mergeClients(opts: MergeOptions): Promise {
.select({ id: interests.id })
.from(interests)
.where(eq(interests.clientId, opts.loserId));
- const loserReservations = await tx
+ const loserTenancies = await tx
.select({ id: berthTenancies.id })
.from(berthTenancies)
.where(eq(berthTenancies.clientId, opts.loserId));
@@ -172,7 +172,7 @@ export async function mergeClients(opts: MergeOptions): Promise {
notes: loserNotes,
tags: loserTags,
interests: loserInterests.map((r) => r.id),
- reservations: loserReservations.map((r) => r.id),
+ tenancies: loserTenancies.map((r) => r.id),
relationshipsAsA: loserRelationshipsAsA,
relationshipsAsB: loserRelationshipsAsB,
fieldChoices: opts.fieldChoices ?? {},
@@ -213,7 +213,7 @@ export async function mergeClients(opts: MergeOptions): Promise {
.returning({ id: interests.id })
).length;
- const movedReservations = (
+ const movedTenancies = (
await tx
.update(berthTenancies)
.set({ clientId: opts.winnerId, updatedAt: new Date() })
@@ -357,7 +357,7 @@ export async function mergeClients(opts: MergeOptions): Promise {
loserId: opts.loserId,
loserName: loserRow.fullName,
movedInterests,
- movedReservations,
+ movedTenancies,
movedContacts,
movedAddresses,
},
@@ -372,7 +372,7 @@ export async function mergeClients(opts: MergeOptions): Promise {
notes: movedNotes,
tags: movedTags,
relationships: movedRelationships,
- reservations: movedReservations,
+ tenancies: movedTenancies,
},
};
});
diff --git a/src/lib/services/gdpr-bundle-builder.ts b/src/lib/services/gdpr-bundle-builder.ts
index 921b2d6e..8ec5b039 100644
--- a/src/lib/services/gdpr-bundle-builder.ts
+++ b/src/lib/services/gdpr-bundle-builder.ts
@@ -57,7 +57,7 @@ export interface GdprBundle {
}>;
interests: Record[];
contactLog: Record[];
- reservations: Record[];
+ tenancies: Record[];
invoices: Record[];
documents: Record[];
files: Record[];
@@ -95,7 +95,7 @@ export async function buildClientBundle(clientId: string, portId: string): Promi
ownedYachts,
membershipRows,
interestRows,
- reservationRows,
+ tenancyRows,
invoiceRows,
documentRows,
fileRows,
@@ -268,7 +268,7 @@ export async function buildClientBundle(clientId: string, portId: string): Promi
})),
interests: interestRows.map(toJsonRow),
contactLog: contactLogRows.map(toJsonRow),
- reservations: reservationRows.map(toJsonRow),
+ tenancies: tenancyRows.map(toJsonRow),
invoices: invoiceRows.map(toJsonRow),
documents: documentRows.map(toJsonRow),
files: fileRows.map(toJsonRow),
@@ -361,7 +361,7 @@ export function renderBundleHtml(bundle: GdprBundle): string {
),
tableSection('Interests', bundle.interests),
tableSection('Contact log', bundle.contactLog),
- tableSection('Reservations', bundle.reservations),
+ tableSection('Tenancies', bundle.tenancies),
tableSection('Invoices', bundle.invoices),
tableSection('Documents', bundle.documents),
tableSection('Files', bundle.files),
diff --git a/tests/e2e/exhaustive/03-reservations.spec.ts b/tests/e2e/exhaustive/03-tenancies.spec.ts
similarity index 72%
rename from tests/e2e/exhaustive/03-reservations.spec.ts
rename to tests/e2e/exhaustive/03-tenancies.spec.ts
index 4aded409..1f574fa9 100644
--- a/tests/e2e/exhaustive/03-reservations.spec.ts
+++ b/tests/e2e/exhaustive/03-tenancies.spec.ts
@@ -3,12 +3,12 @@ import { test, expect } from '@playwright/test';
import { clickEverythingOnPage } from '../../helpers/click-everything';
import { login, navigateTo, PORT_SLUG } from '../smoke/helpers';
-test.describe('exhaustive: berth reservations', () => {
+test.describe('exhaustive: berth tenancies', () => {
test.beforeEach(async ({ page }) => {
await login(page, 'super_admin');
});
- test('berths list - reservation-related affordances are clickable', async ({ page }) => {
+ test('berths list - tenancy-related affordances are clickable', async ({ page }) => {
await navigateTo(page, '/berths');
await page.waitForLoadState('networkidle');
@@ -16,7 +16,7 @@ test.describe('exhaustive: berth reservations', () => {
expect(result.errors, JSON.stringify(result.errors, null, 2)).toEqual([]);
});
- test('berth detail - reservations tab opens and is interactive', async ({ page }) => {
+ test('berth detail - tenancies tab opens and is interactive', async ({ page }) => {
await navigateTo(page, '/berths');
await page.waitForLoadState('networkidle');
@@ -29,9 +29,9 @@ test.describe('exhaustive: berth reservations', () => {
await page.waitForURL(new RegExp(`/${PORT_SLUG}/berths/[^/]+`), { timeout: 10_000 });
await page.waitForLoadState('networkidle');
- const reservationsTab = page.getByRole('tab', { name: /reservations/i }).first();
- if (await reservationsTab.isVisible({ timeout: 3000 }).catch(() => false)) {
- await reservationsTab.click();
+ const tenanciesTab = page.getByRole('tab', { name: /tenancies/i }).first();
+ if (await tenanciesTab.isVisible({ timeout: 3000 }).catch(() => false)) {
+ await tenanciesTab.click();
await page.waitForLoadState('networkidle');
}
@@ -39,7 +39,7 @@ test.describe('exhaustive: berth reservations', () => {
expect(result.errors, JSON.stringify(result.errors, null, 2)).toEqual([]);
});
- test('reserve dialog opens and closes', async ({ page }) => {
+ test('create-tenancy dialog opens and closes', async ({ page }) => {
await navigateTo(page, '/berths');
await page.waitForLoadState('networkidle');
const firstRow = page.locator('tbody tr a, tbody tr button').first();
@@ -50,9 +50,9 @@ test.describe('exhaustive: berth reservations', () => {
await firstRow.click();
await page.waitForLoadState('networkidle');
- const reserveBtn = page.getByRole('button', { name: /reserve|new reservation/i }).first();
- if (await reserveBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
- await reserveBtn.click();
+ const createBtn = page.getByRole('button', { name: /create tenancy|reserve/i }).first();
+ if (await createBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
+ await createBtn.click();
const dialog = page.getByRole('dialog');
await expect(dialog).toBeVisible({ timeout: 5000 });
const cancel = dialog.getByRole('button', { name: /cancel|close/i }).first();
diff --git a/tests/e2e/exhaustive/08-portal.spec.ts b/tests/e2e/exhaustive/08-portal.spec.ts
index 39007f3e..a904b0bf 100644
--- a/tests/e2e/exhaustive/08-portal.spec.ts
+++ b/tests/e2e/exhaustive/08-portal.spec.ts
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
import { clickEverythingOnPage } from '../../helpers/click-everything';
import { login } from '../smoke/helpers';
-const PORTAL_PAGES = ['/portal/yachts', '/portal/memberships', '/portal/my-reservations'];
+const PORTAL_PAGES = ['/portal/yachts', '/portal/memberships', '/portal/my-tenancies'];
test.describe('exhaustive: client portal', () => {
test.beforeEach(async ({ page }) => {
diff --git a/tests/integration/api/tenancies.test.ts b/tests/integration/api/tenancies.test.ts
index 878203b0..1a5bc325 100644
--- a/tests/integration/api/tenancies.test.ts
+++ b/tests/integration/api/tenancies.test.ts
@@ -2,13 +2,13 @@ import { describe, it, expect } from 'vitest';
import { eq } from 'drizzle-orm';
import {
- createHandler as createReservationHandler,
- listHandler as listReservationsHandler,
+ createHandler as createTenancyHandler,
+ listHandler as listTenanciesHandler,
} from '@/app/api/v1/berths/[id]/tenancies/handlers';
import {
- getHandler as getReservationHandler,
- patchHandler as patchReservationHandler,
- deleteHandler as deleteReservationHandler,
+ getHandler as getTenancyHandler,
+ patchHandler as patchTenancyHandler,
+ deleteHandler as deleteTenancyHandler,
} from '@/app/api/v1/tenancies/[id]/handlers';
import { db } from '@/lib/db';
import { berthTenancies } from '@/lib/db/schema/tenancies';
@@ -53,7 +53,7 @@ describe('POST /api/v1/berths/[id]/tenancies', () => {
startDate: new Date().toISOString(),
},
});
- const res = await createReservationHandler(req, ctx, { id: berth.id });
+ const res = await createTenancyHandler(req, ctx, { id: berth.id });
expect(res.status).toBe(201);
const body = (await res.json()) as any;
expect(body.data.berthId).toBe(berth.id);
@@ -81,7 +81,7 @@ describe('POST /api/v1/berths/[id]/tenancies', () => {
startDate: new Date().toISOString(),
},
});
- const res = await createReservationHandler(req, ctx, { id: berth.id });
+ const res = await createTenancyHandler(req, ctx, { id: berth.id });
expect(res.status).toBe(400);
});
@@ -105,7 +105,7 @@ describe('POST /api/v1/berths/[id]/tenancies', () => {
startDate: new Date().toISOString(),
},
});
- const res = await createReservationHandler(req, ctxB, { id: berthA.id });
+ const res = await createTenancyHandler(req, ctxB, { id: berthA.id });
expect(res.status).toBe(404);
});
@@ -129,7 +129,7 @@ describe('POST /api/v1/berths/[id]/tenancies', () => {
startDate: new Date().toISOString(),
},
});
- const res = await createReservationHandler(req, ctx, { id: urlBerth.id });
+ const res = await createTenancyHandler(req, ctx, { id: urlBerth.id });
expect(res.status).toBe(201);
const body = (await res.json()) as any;
expect(body.data.berthId).toBe(urlBerth.id);
@@ -153,7 +153,7 @@ describe('GET /api/v1/berths/[id]/tenancies', () => {
const ctx = makeMockCtx({ portId: port.id, permissions: makeFullPermissions() });
// Create a reservation for berthA.
- await createReservationHandler(
+ await createTenancyHandler(
makeMockRequest('POST', `http://localhost/api/v1/berths/${berthA.id}/tenancies`, {
body: {
clientId: client.id,
@@ -166,7 +166,7 @@ describe('GET /api/v1/berths/[id]/tenancies', () => {
);
// Create a reservation for berthB.
- await createReservationHandler(
+ await createTenancyHandler(
makeMockRequest('POST', `http://localhost/api/v1/berths/${berthB.id}/tenancies`, {
body: {
clientId: client.id,
@@ -178,7 +178,7 @@ describe('GET /api/v1/berths/[id]/tenancies', () => {
{ id: berthB.id },
);
- const res = await listReservationsHandler(
+ const res = await listTenanciesHandler(
makeMockRequest('GET', `http://localhost/api/v1/berths/${berthA.id}/tenancies`),
ctx,
{ id: berthA.id },
@@ -204,7 +204,7 @@ describe('GET /api/v1/tenancies/[id]', () => {
});
const ctx = makeMockCtx({ portId: port.id, permissions: makeFullPermissions() });
- const createRes = await createReservationHandler(
+ const createRes = await createTenancyHandler(
makeMockRequest('POST', `http://localhost/api/v1/berths/${berth.id}/tenancies`, {
body: {
clientId: client.id,
@@ -217,7 +217,7 @@ describe('GET /api/v1/tenancies/[id]', () => {
);
const reservation = ((await createRes.json()) as any).data;
- const res = await getReservationHandler(
+ const res = await getTenancyHandler(
makeMockRequest('GET', `http://localhost/api/v1/tenancies/${reservation.id}`),
ctx,
{ id: reservation.id },
@@ -239,7 +239,7 @@ describe('GET /api/v1/tenancies/[id]', () => {
});
const ctxA = makeMockCtx({ portId: portA.id, permissions: makeFullPermissions() });
- const createRes = await createReservationHandler(
+ const createRes = await createTenancyHandler(
makeMockRequest('POST', `http://localhost/api/v1/berths/${berth.id}/tenancies`, {
body: {
clientId: client.id,
@@ -253,7 +253,7 @@ describe('GET /api/v1/tenancies/[id]', () => {
const reservation = ((await createRes.json()) as any).data;
const ctxB = makeMockCtx({ portId: portB.id, permissions: makeFullPermissions() });
- const res = await getReservationHandler(
+ const res = await getTenancyHandler(
makeMockRequest('GET', `http://localhost/api/v1/tenancies/${reservation.id}`),
ctxB,
{ id: reservation.id },
@@ -276,7 +276,7 @@ describe('PATCH /api/v1/tenancies/[id]', () => {
});
const ctx = makeMockCtx({ portId: port.id, permissions: makeFullPermissions() });
- const createRes = await createReservationHandler(
+ const createRes = await createTenancyHandler(
makeMockRequest('POST', `http://localhost/api/v1/berths/${berth.id}/tenancies`, {
body: {
clientId: client.id,
@@ -293,7 +293,7 @@ describe('PATCH /api/v1/tenancies/[id]', () => {
it('activate: pending → active (200)', async () => {
const { ctx, reservation } = await seedReservation();
- const res = await patchReservationHandler(
+ const res = await patchTenancyHandler(
makeMockRequest('PATCH', `http://localhost/api/v1/tenancies/${reservation.id}`, {
body: { action: 'activate' },
}),
@@ -309,7 +309,7 @@ describe('PATCH /api/v1/tenancies/[id]', () => {
const { ctx, reservation } = await seedReservation();
// First activate.
- await patchReservationHandler(
+ await patchTenancyHandler(
makeMockRequest('PATCH', `http://localhost/api/v1/tenancies/${reservation.id}`, {
body: { action: 'activate' },
}),
@@ -319,7 +319,7 @@ describe('PATCH /api/v1/tenancies/[id]', () => {
// Then end.
const endDate = new Date('2027-01-01T00:00:00.000Z');
- const res = await patchReservationHandler(
+ const res = await patchTenancyHandler(
makeMockRequest('PATCH', `http://localhost/api/v1/tenancies/${reservation.id}`, {
body: {
action: 'end',
@@ -338,7 +338,7 @@ describe('PATCH /api/v1/tenancies/[id]', () => {
it('cancel: pending → cancelled (200)', async () => {
const { ctx, reservation } = await seedReservation();
- const res = await patchReservationHandler(
+ const res = await patchTenancyHandler(
makeMockRequest('PATCH', `http://localhost/api/v1/tenancies/${reservation.id}`, {
body: { action: 'cancel', reason: 'client changed mind' },
}),
@@ -354,7 +354,7 @@ describe('PATCH /api/v1/tenancies/[id]', () => {
const { ctx, reservation } = await seedReservation();
// pending → active.
- await patchReservationHandler(
+ await patchTenancyHandler(
makeMockRequest('PATCH', `http://localhost/api/v1/tenancies/${reservation.id}`, {
body: { action: 'activate' },
}),
@@ -362,7 +362,7 @@ describe('PATCH /api/v1/tenancies/[id]', () => {
{ id: reservation.id },
);
// active → ended.
- await patchReservationHandler(
+ await patchTenancyHandler(
makeMockRequest('PATCH', `http://localhost/api/v1/tenancies/${reservation.id}`, {
body: {
action: 'end',
@@ -374,7 +374,7 @@ describe('PATCH /api/v1/tenancies/[id]', () => {
);
// ended → activate should fail.
- const res = await patchReservationHandler(
+ const res = await patchTenancyHandler(
makeMockRequest('PATCH', `http://localhost/api/v1/tenancies/${reservation.id}`, {
body: { action: 'activate' },
}),
@@ -386,7 +386,7 @@ describe('PATCH /api/v1/tenancies/[id]', () => {
it('returns 400 on invalid body shape (action missing)', async () => {
const { ctx, reservation } = await seedReservation();
- const res = await patchReservationHandler(
+ const res = await patchTenancyHandler(
makeMockRequest('PATCH', `http://localhost/api/v1/tenancies/${reservation.id}`, {
body: { notes: 'noop' },
}),
@@ -406,7 +406,7 @@ describe('PATCH /api/v1/tenancies/[id]', () => {
tenancies: { view: true, manage: false, cancel: true },
},
});
- const res = await patchReservationHandler(
+ const res = await patchTenancyHandler(
makeMockRequest('PATCH', `http://localhost/api/v1/tenancies/${reservation.id}`, {
body: { action: 'activate' },
}),
@@ -423,7 +423,7 @@ describe('PATCH /api/v1/tenancies/[id]', () => {
portId: port.id,
permissions: makeSalesAgentPermissions(),
});
- const res = await patchReservationHandler(
+ const res = await patchTenancyHandler(
makeMockRequest('PATCH', `http://localhost/api/v1/tenancies/${reservation.id}`, {
body: { action: 'cancel', reason: 'test' },
}),
@@ -433,7 +433,7 @@ describe('PATCH /api/v1/tenancies/[id]', () => {
expect(res.status).toBe(403);
// But activate succeeds with the same permissions set.
- const activateRes = await patchReservationHandler(
+ const activateRes = await patchTenancyHandler(
makeMockRequest('PATCH', `http://localhost/api/v1/tenancies/${reservation.id}`, {
body: { action: 'activate' },
}),
@@ -458,7 +458,7 @@ describe('DELETE /api/v1/tenancies/[id]', () => {
});
const ctx = makeMockCtx({ portId: port.id, permissions: makeFullPermissions() });
- const createRes = await createReservationHandler(
+ const createRes = await createTenancyHandler(
makeMockRequest('POST', `http://localhost/api/v1/berths/${berth.id}/tenancies`, {
body: {
clientId: client.id,
@@ -471,7 +471,7 @@ describe('DELETE /api/v1/tenancies/[id]', () => {
);
const reservation = ((await createRes.json()) as any).data;
- const delRes = await deleteReservationHandler(
+ const delRes = await deleteTenancyHandler(
makeMockRequest('DELETE', `http://localhost/api/v1/tenancies/${reservation.id}`),
ctx,
{ id: reservation.id },
diff --git a/tests/unit/services/berth-tenancies.test.ts b/tests/unit/services/berth-tenancies.test.ts
index 99462666..68af18dd 100644
--- a/tests/unit/services/berth-tenancies.test.ts
+++ b/tests/unit/services/berth-tenancies.test.ts
@@ -331,7 +331,7 @@ describe('berth-tenancies.service - lifecycle transitions', () => {
// ─── listTenancies ────────────────────────────────────────────────────────
describe('berth-tenancies.service - listTenancies', () => {
- async function makeReservation(portId: string, opts?: { berthId?: string }) {
+ async function makeTenancyLocal(portId: string, opts?: { berthId?: string }) {
const berth = opts?.berthId ? { id: opts.berthId } : await makeBerth({ portId });
const client = await makeClient({ portId });
const yacht = await makeYacht({ portId, ownerType: 'client', ownerId: client.id });
@@ -351,8 +351,8 @@ describe('berth-tenancies.service - listTenancies', () => {
it('is tenant-scoped', async () => {
const portA = await makePort();
const portB = await makePort();
- const resA = await makeReservation(portA.id);
- await makeReservation(portB.id);
+ const resA = await makeTenancyLocal(portA.id);
+ await makeTenancyLocal(portB.id);
const result = await listTenancies(portA.id, {
page: 1,
@@ -367,8 +367,8 @@ describe('berth-tenancies.service - listTenancies', () => {
it('filters by status', async () => {
const port = await makePort();
- const resPending = await makeReservation(port.id);
- const resActive = await makeReservation(port.id);
+ const resPending = await makeTenancyLocal(port.id);
+ const resActive = await makeTenancyLocal(port.id);
await activate(resActive.id, port.id, {}, makeAuditMeta({ portId: port.id }));
const activeList = await listTenancies(port.id, {