feat(tenders): add Tender Management module (SRS, backend, frontend)
- SRS document: docs/SRS_TENDER_MANAGEMENT.md - Prisma: Tender, TenderDirective models; Deal.sourceTenderId; Attachment.tenderId/tenderDirectiveId - Backend: tenders module (CRUD, duplicate check, directives, notifications, file upload, convert-to-deal) - Frontend: tenders list, detail, create/edit forms, directives, convert to deal, i18n (en/ar), dashboard card - Seed: tenders permissions for admin and sales positions - Auth: admin.service findFirst for email check (Prisma compatibility) Made-with: Cursor
This commit is contained in:
@@ -70,7 +70,10 @@ model User {
|
||||
projectMembers ProjectMember[]
|
||||
campaigns Campaign[]
|
||||
userRoles UserRole[]
|
||||
|
||||
tendersCreated Tender[]
|
||||
tenderDirectivesIssued TenderDirective[]
|
||||
tenderDirectivesCompleted TenderDirective[] @relation("TenderDirectiveCompletedBy")
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
@@ -199,7 +202,8 @@ model Employee {
|
||||
purchaseRequests PurchaseRequest[]
|
||||
leaveEntitlements LeaveEntitlement[]
|
||||
employeeContracts EmployeeContract[]
|
||||
|
||||
tenderDirectivesAssigned TenderDirective[]
|
||||
|
||||
@@index([departmentId])
|
||||
@@index([positionId])
|
||||
@@index([status])
|
||||
@@ -610,7 +614,8 @@ model Contact {
|
||||
deals Deal[]
|
||||
attachments Attachment[]
|
||||
notes Note[]
|
||||
|
||||
tenders Tender[]
|
||||
|
||||
@@index([type])
|
||||
@@index([status])
|
||||
@@index([email])
|
||||
@@ -705,10 +710,14 @@ model Deal {
|
||||
|
||||
// Status
|
||||
status String @default("ACTIVE") // ACTIVE, WON, LOST, CANCELLED, ARCHIVED
|
||||
|
||||
|
||||
// Source (when converted from Tender)
|
||||
sourceTenderId String? @unique
|
||||
sourceTender Tender? @relation(fields: [sourceTenderId], references: [id])
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
|
||||
// Relations
|
||||
quotes Quote[]
|
||||
costSheets CostSheet[]
|
||||
@@ -718,7 +727,7 @@ model Deal {
|
||||
contracts Contract[]
|
||||
invoices Invoice[]
|
||||
commissions Commission[]
|
||||
|
||||
|
||||
@@index([contactId])
|
||||
@@index([ownerId])
|
||||
@@index([pipelineId])
|
||||
@@ -873,6 +882,66 @@ model Invoice {
|
||||
@@map("invoices")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// TENDER MANAGEMENT - إدارة المناقصات
|
||||
// ============================================
|
||||
|
||||
model Tender {
|
||||
id String @id @default(uuid())
|
||||
tenderNumber String @unique
|
||||
issuingBodyName String
|
||||
title String
|
||||
termsValue Decimal @db.Decimal(15, 2)
|
||||
bondValue Decimal @db.Decimal(15, 2)
|
||||
announcementDate DateTime @db.Date
|
||||
closingDate DateTime @db.Date
|
||||
announcementLink String?
|
||||
source String // GOVERNMENT_SITE, OFFICIAL_GAZETTE, PERSONAL, PARTNER, WHATSAPP_TELEGRAM, PORTAL, EMAIL, MANUAL
|
||||
sourceOther String? // Free text when source is MANUAL or other
|
||||
announcementType String // FIRST, RE_ANNOUNCEMENT_2, RE_ANNOUNCEMENT_3, RE_ANNOUNCEMENT_4
|
||||
notes String?
|
||||
status String @default("ACTIVE") // ACTIVE, CONVERTED_TO_DEAL, CANCELLED
|
||||
contactId String? // Optional link to Contact (issuing body)
|
||||
contact Contact? @relation(fields: [contactId], references: [id])
|
||||
createdById String
|
||||
createdBy User @relation(fields: [createdById], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
directives TenderDirective[]
|
||||
attachments Attachment[]
|
||||
convertedDeal Deal?
|
||||
@@index([tenderNumber])
|
||||
@@index([status])
|
||||
@@index([createdById])
|
||||
@@index([announcementDate])
|
||||
@@index([closingDate])
|
||||
@@map("tenders")
|
||||
}
|
||||
|
||||
model TenderDirective {
|
||||
id String @id @default(uuid())
|
||||
tenderId String
|
||||
tender Tender @relation(fields: [tenderId], references: [id], onDelete: Cascade)
|
||||
type String // BUY_TERMS, VISIT_CLIENT, MEET_COMMITTEE, PREPARE_TO_BID
|
||||
notes String?
|
||||
assignedToEmployeeId String
|
||||
assignedToEmployee Employee @relation(fields: [assignedToEmployeeId], references: [id])
|
||||
issuedById String
|
||||
issuedBy User @relation(fields: [issuedById], references: [id])
|
||||
status String @default("PENDING") // PENDING, IN_PROGRESS, COMPLETED, CANCELLED
|
||||
completedAt DateTime?
|
||||
completionNotes String?
|
||||
completedById String?
|
||||
completedBy User? @relation("TenderDirectiveCompletedBy", fields: [completedById], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
attachments Attachment[]
|
||||
@@index([tenderId])
|
||||
@@index([assignedToEmployeeId])
|
||||
@@index([status])
|
||||
@@map("tender_directives")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// MODULE 3: INVENTORY & ASSETS
|
||||
// ============================================
|
||||
@@ -1420,11 +1489,11 @@ model Note {
|
||||
|
||||
model Attachment {
|
||||
id String @id @default(uuid())
|
||||
|
||||
|
||||
// Related Entity
|
||||
entityType String
|
||||
entityId String
|
||||
|
||||
|
||||
// Relations
|
||||
contactId String?
|
||||
contact Contact? @relation(fields: [contactId], references: [id])
|
||||
@@ -1434,7 +1503,11 @@ model Attachment {
|
||||
project Project? @relation(fields: [projectId], references: [id])
|
||||
taskId String?
|
||||
task Task? @relation(fields: [taskId], references: [id])
|
||||
|
||||
tenderId String?
|
||||
tender Tender? @relation(fields: [tenderId], references: [id], onDelete: Cascade)
|
||||
tenderDirectiveId String?
|
||||
tenderDirective TenderDirective? @relation(fields: [tenderDirectiveId], references: [id], onDelete: Cascade)
|
||||
|
||||
// File Info
|
||||
fileName String
|
||||
originalName String
|
||||
@@ -1442,19 +1515,21 @@ model Attachment {
|
||||
size Int
|
||||
path String
|
||||
url String?
|
||||
|
||||
|
||||
// Metadata
|
||||
description String?
|
||||
category String?
|
||||
|
||||
|
||||
uploadedBy String
|
||||
uploadedAt DateTime @default(now())
|
||||
|
||||
|
||||
@@index([entityType, entityId])
|
||||
@@index([contactId])
|
||||
@@index([dealId])
|
||||
@@index([projectId])
|
||||
@@index([taskId])
|
||||
@@index([tenderId])
|
||||
@@index([tenderDirectiveId])
|
||||
@@map("attachments")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user