fix(audit): H4 (reservation signing berth rule) + H13 (manual EOI-sign stage parity)
H4: reservation_agreement completion fired the contract_signed berth rule, flipping the berth to 'sold' one-to-two stages early. Add a dedicated reservation_signed berth trigger (defaults to under_offer) and fire it. H13: the manual signed-EOI upload path advanced only to 'eoi' via the ungated helper while the Documenso-webhook path advanced to 'reservation'; both now use advanceStageIfBehindGated(..., 'reservation', 'eoi_signed') so manually- and webhook-signed deals reach the same stage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import { logger } from '@/lib/logger';
|
|||||||
export type BerthRuleTrigger =
|
export type BerthRuleTrigger =
|
||||||
| 'eoi_sent'
|
| 'eoi_sent'
|
||||||
| 'eoi_signed'
|
| 'eoi_signed'
|
||||||
|
| 'reservation_signed'
|
||||||
| 'deposit_received'
|
| 'deposit_received'
|
||||||
| 'contract_signed'
|
| 'contract_signed'
|
||||||
| 'interest_archived'
|
| 'interest_archived'
|
||||||
@@ -39,6 +40,10 @@ interface RuleConfig {
|
|||||||
const DEFAULT_RULES: Record<BerthRuleTrigger, RuleConfig> = {
|
const DEFAULT_RULES: Record<BerthRuleTrigger, RuleConfig> = {
|
||||||
eoi_sent: { mode: 'suggest', targetStatus: 'under_offer' },
|
eoi_sent: { mode: 'suggest', targetStatus: 'under_offer' },
|
||||||
eoi_signed: { mode: 'auto', targetStatus: 'under_offer' },
|
eoi_signed: { mode: 'auto', targetStatus: 'under_offer' },
|
||||||
|
// Reservation agreement signed — a commitment short of sale, so the berth
|
||||||
|
// stays Under offer (audit H4); previously reused the contract_signed rule
|
||||||
|
// and flipped it to Sold prematurely.
|
||||||
|
reservation_signed: { mode: 'auto', targetStatus: 'under_offer' },
|
||||||
deposit_received: { mode: 'auto', targetStatus: 'sold' },
|
deposit_received: { mode: 'auto', targetStatus: 'sold' },
|
||||||
contract_signed: { mode: 'auto', targetStatus: 'sold' },
|
contract_signed: { mode: 'auto', targetStatus: 'sold' },
|
||||||
interest_archived: { mode: 'suggest', targetStatus: 'available' },
|
interest_archived: { mode: 'suggest', targetStatus: 'available' },
|
||||||
|
|||||||
@@ -988,13 +988,18 @@ export async function uploadSignedManually(
|
|||||||
if (interest) {
|
if (interest) {
|
||||||
void evaluateRule('eoi_signed', doc.interestId, portId, meta);
|
void evaluateRule('eoi_signed', doc.interestId, portId, meta);
|
||||||
|
|
||||||
// Stage stays at 'eoi' - sub-status badge flips to "signed".
|
// EOI signed = formal commitment → advance to 'reservation' via the
|
||||||
void advanceStageIfBehind(
|
// GATED helper, matching the Documenso-webhook path (audit H13).
|
||||||
|
// Previously this manual-upload path used the ungated helper and only
|
||||||
|
// reached 'eoi', so manually-signed deals lagged a stage behind
|
||||||
|
// webhook-signed ones and skewed funnel/stage-duration reports.
|
||||||
|
void advanceStageIfBehindGated(
|
||||||
doc.interestId,
|
doc.interestId,
|
||||||
portId,
|
portId,
|
||||||
'eoi',
|
'reservation',
|
||||||
meta,
|
meta,
|
||||||
'Signed EOI uploaded manually',
|
'Signed EOI uploaded manually',
|
||||||
|
'eoi_signed',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1680,7 +1685,11 @@ export async function handleDocumentCompleted(eventData: { documentId: string; p
|
|||||||
'reservation_signed',
|
'reservation_signed',
|
||||||
);
|
);
|
||||||
void import('@/lib/services/berth-rules-engine').then(({ evaluateRule }) =>
|
void import('@/lib/services/berth-rules-engine').then(({ evaluateRule }) =>
|
||||||
evaluateRule('contract_signed', doc.interestId!, doc.portId, systemMeta),
|
// Reservation signing is NOT contract signing — firing 'contract_signed'
|
||||||
|
// here flipped the berth to 'sold' one-to-two stages early (audit H4).
|
||||||
|
// Use the dedicated 'reservation_signed' trigger (defaults to
|
||||||
|
// 'under_offer').
|
||||||
|
evaluateRule('reservation_signed', doc.interestId!, doc.portId, systemMeta),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Tenancies P3 — auto-create pending tenancies (one per in-bundle berth).
|
// Tenancies P3 — auto-create pending tenancies (one per in-bundle berth).
|
||||||
|
|||||||
Reference in New Issue
Block a user