edit status of tenders

This commit is contained in:
Aya
2026-05-07 16:16:31 +03:00
parent e01e351713
commit 31c59a3c9f

View File

@@ -86,8 +86,52 @@ class TendersService {
return `TND-${year}-${seq}`;
}
private readonly EXTRA_META_START = '[EXTRA_TENDER_META]';
private readonly EXTRA_META_START = '[EXTRA_TENDER_META]';
private readonly EXTRA_META_END = '[/EXTRA_TENDER_META]';
private getCompanyTodayDate(): Date {
const parts = new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Riyadh',
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).formatToParts(new Date());
const year = parts.find((p) => p.type === 'year')?.value;
const month = parts.find((p) => p.type === 'month')?.value;
const day = parts.find((p) => p.type === 'day')?.value;
return new Date(`${year}-${month}-${day}T00:00:00.000Z`);
}
private toDateOnly(value: Date | string | null | undefined): Date | null {
if (!value) return null;
const date = value instanceof Date ? value : new Date(value);
if (Number.isNaN(date.getTime())) return null;
const year = date.getUTCFullYear();
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
const day = String(date.getUTCDate()).padStart(2, '0');
return new Date(`${year}-${month}-${day}T00:00:00.000Z`);
}
private getEffectiveTenderStatus(tender: {
status?: string | null;
closingDate?: Date | string | null;
}) {
if (tender.status === 'ACTIVE') {
const closingDate = this.toDateOnly(tender.closingDate);
const today = this.getCompanyTodayDate();
if (closingDate && closingDate < today) {
return 'EXPIRED';
}
}
return tender.status || 'ACTIVE';
}
private extractTenderExtraMeta(notes?: string | null) {
if (!notes) {
@@ -208,11 +252,17 @@ class TendersService {
return cleanedNotes ? `${cleanedNotes}\n${metaBlock}` : metaBlock;
}
private mapTenderExtraFields<T extends { notes?: string | null; bondValue?: any }>(tender: T) {
const { cleanNotes, meta } = this.extractTenderExtraMeta(tender.notes);
private mapTenderExtraFields<T extends {
notes?: string | null;
bondValue?: any;
status?: string | null;
closingDate?: Date | string | null;
}>(tender: T) {
const { cleanNotes, meta } = this.extractTenderExtraMeta(tender.notes);
return {
...tender,
status: this.getEffectiveTenderStatus(tender),
notes: cleanNotes || null,
initialBondValue: meta.initialBondValue ?? Number(tender.bondValue ?? 0),
finalBondValue: meta.finalBondValue ?? null,
@@ -345,7 +395,15 @@ class TendersService {
{ issuingBodyName: { contains: filters.search, mode: 'insensitive' } },
];
}
if (filters.status) where.status = filters.status;
if (filters.status === 'EXPIRED') {
where.status = 'ACTIVE';
where.closingDate = { lt: this.getCompanyTodayDate() };
} else if (filters.status === 'ACTIVE') {
where.status = 'ACTIVE';
where.closingDate = { gte: this.getCompanyTodayDate() };
} else if (filters.status) {
where.status = filters.status;
}
if (filters.source) where.source = filters.source;
if (filters.announcementType) where.announcementType = filters.announcementType;
@@ -624,6 +682,9 @@ class TendersService {
if (tender.status === 'CONVERTED_TO_DEAL') {
throw new AppError(400, 'Tender already converted to deal');
}
if (this.getEffectiveTenderStatus(tender) === 'EXPIRED') {
throw new AppError(400, 'Cannot convert expired tender to deal');
}
const pipeline = await prisma.pipeline.findUnique({
where: { id: data.pipelineId },