fix issue

This commit is contained in:
Marshal
2026-07-16 01:05:57 +00:00
parent 41fe04652f
commit fe29b38377
18 changed files with 518 additions and 51 deletions

View File

@@ -60,6 +60,17 @@ export interface ContractDocumentDraft {
*/
const CONTRACT_VALIDITY_PERIODS_CODE = 'contract_validity_periods';
/**
* Mask a phone for display — keep the last 4 digits, star the rest
* (`+251986680099` → `•••••••0099`). Used to tell the customer WHERE the signing
* code went without echoing the company's full registered number back to the UI.
*/
function maskPhone(phone: string): string {
const trimmed = phone.trim();
if (trimmed.length <= 4) return trimmed;
return `${'•'.repeat(trimmed.length - 4)}${trimmed.slice(-4)}`;
}
/** Status-machine guard mirroring booking-status.util. */
function assertContractStatus(contract: Contract, allowed: string[]): void {
if (!allowed.includes(contract.status)) {
@@ -784,6 +795,36 @@ export class ContractTransitionService {
}
}
/**
* Send the sudo-mode signing OTP to the CONTRACT COMPANY's registered phone —
* the same number {@link sign} verifies against. The client never picks the
* number (that is the H12(b) trust property): it only asks us to send, and we
* resolve the phone from the contract. Returns a masked hint so the UI can
* say where the code went without exposing the full number.
*/
async sendSigningOtp(
contractId: string,
options: { signerUserId?: string },
): Promise<{ sentTo: string }> {
const contract = await this.contractsService.findById(contractId);
// Same ownership gate as signing — only the owning company's customer may
// trigger a code for this contract.
await this.contractsService.assertCustomerCanAccessContract(
options.signerUserId,
contract,
);
assertContractStatus(contract, ['CONTRACT_READY']);
const companyPhone = contract.company?.phone?.trim();
if (!companyPhone) {
throw new BadRequestException(
'The contract company has no registered phone on file to send the signing OTP to',
);
}
await this.otpService.sendOtp({ phone: companyPhone });
return { sentTo: maskPhone(companyPhone) };
}
/** Customer signs the ready contract → SIGNED_CUSTOMER. */
async sign(
contractId: string,