Last active 2 weeks ago

Revision 6fcfd136321428c5144855099e7dd88abf7b8afb

schema.prisma Raw
1// This is your Prisma schema file,
2// learn more about it in the docs: https://pris.ly/d/prisma-schema
3
4// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
5// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
6
7generator client {
8 provider = "prisma-client-js"
9 previewFeatures = []
10}
11
12datasource db {
13 provider = "mysql"
14 url = env("DATABASE_URL")
15}
16
17/**
18 * Enums
19 */
20// Enums for Document
21enum DocumentType {
22 Design_Development
23 Detail_Engineering_Design
24 For_Construction
25 Shop_Drawing
26 Sitemap
27 As_Build_Drawing
28
29 Work_Permit
30 Method_Of_Work
31 Material_Testing
32 Site_Instruction
33}
34
35enum VersionStatus {
36 IN_REVIEW
37 APPROVED
38 APPROVED_WITH_NOTES
39 NEED_IMPROVEMENT
40 REJECTED
41 REJECTED_WITH_NOTES
42 REJECTED_RESUBMISSION
43 SCHEDULED // version reserved on document-service for a not-yet-sent scheduled transmittal
44}
45
46enum RevisionStatus {
47 IN_REVIEW
48 APPROVED
49 APPROVED_WITH_NOTES
50 NEED_IMPROVEMENT
51 REJECTED
52 REJECTED_WITH_NOTES
53 REJECTED_RESUBMISSION
54}
55
56enum DocumentPageStatus {
57 queue // Initial state
58 extracting_preview // Phase 1: Extracting preview (150 DPI)
59 preview_ready // Phase 1 complete: Preview available
60 extracting_quality // Phase 2: Extracting full quality
61 compressing // Optional: WebP compression
62 ready // Final state: Full quality available
63}
64
65// Enums for Queue
66enum QueueDownloadStatus {
67 PENDING
68 IN_PROGRESS
69 COMPLETED
70 FAILED
71 EXPIRED
72}
73
74enum QueueDownloadType {
75 DIRECTORY
76 DOCUMENT
77}
78
79enum QueueExtractorStatus {
80 PENDING
81 IN_PROGRESS
82 COMPLETED
83 FAILED
84 EXPIRED
85}
86
87// Enums for Change Log (CDC)
88enum Operation {
89 CREATE
90 UPDATE
91 DELETE
92}
93
94enum AutodeskTranslationStatus {
95 PENDING
96 IN_PROGRESS
97 SUCCESS
98 FAILED
99}
100
101/**
102 * Models
103 */
104model Folder {
105 id String @id @unique @db.VarChar(50)
106 projectId String @db.VarChar(100)
107 parentId String? @db.VarChar(50)
108 modelType DocumentType?
109 name String @db.VarChar(255)
110 description String? @db.VarChar(500)
111 companyId String? @db.VarChar(50)
112 createdBy String? @db.VarChar(150)
113 createdAt DateTime @default(now())
114 updatedBy String? @db.VarChar(150)
115 updatedAt DateTime?
116 inTrash Boolean @default(false)
117
118 parent Folder? @relation("ParentChildren", fields: [parentId], references: [id], onDelete: Cascade)
119 children Folder[] @relation("ParentChildren")
120 documentVersions Version[]
121 documentRevisions Revision[]
122
123 @@index([projectId, parentId, modelType, companyId])
124 @@map("folders")
125}
126
127model Document {
128 id String @id @unique @db.VarChar(50)
129 projectId String @db.VarChar(100)
130 companyId String? @db.VarChar(50)
131 modelType DocumentType?
132 filename String @db.VarChar(255)
133 inTrash Boolean @default(false)
134 activeVersionId String? @unique @db.VarChar(50)
135 activeRevisionId String? @unique @db.VarChar(50)
136 activeTransmittalId String? @db.VarChar(255)
137
138 uploadedBy String? @db.VarChar(150)
139 uploadedAt DateTime? @default(now())
140 updatedBy String? @db.VarChar(150)
141 updatedAt DateTime?
142
143 activeVersion Version? @relation("DocumentActiveVersion", fields: [activeVersionId], references: [id])
144 activeRevision Revision? @relation("DocumentActiveRevision", fields: [activeRevisionId], references: [id])
145 versions Version[] @relation("DocumentVersions")
146 revisions Revision[] @relation("DocumentRevisions")
147 relations Document[] @relation("DocumentRelation")
148 related Document[] @relation("DocumentRelation")
149 comments DocumentComments[]
150 pages DocumentPages[]
151 document3DMetadata Document3DMetadata[] @relation("Document3DMetadatas")
152
153 @@index([projectId, modelType, activeTransmittalId])
154 @@map("documents")
155}
156
157model DocumentPages {
158 id String @id @unique @db.VarChar(50)
159 documentId String @db.VarChar(50)
160 versionId String @db.VarChar(50)
161 revisionId String @db.VarChar(50)
162 queueId String? @db.VarChar(50) // Optional: tracks which queue job created this page
163 status DocumentPageStatus @default(queue)
164
165 pageOrder Int @default(0)
166 pageName String? @db.VarChar(255)
167 pageType String? @db.VarChar(255)
168 pageContent String? @db.Text
169 pageWidth Float @default(0)
170 pageHeight Float @default(0)
171
172 filePath String? @db.VarChar(512) // Increased from 255 for SHA256 hash-based paths
173 fileSize Float @default(0)
174 fileExtension String? @db.VarChar(10)
175 fileMimeType String? @db.VarChar(255)
176
177 // DPI tracking for two-phase extraction
178 dpi Int @default(150) // Current DPI (150 for preview, 300-600 for quality)
179 targetDpi Int? // Target DPI detected from PDF analysis
180 extractionPreset String? @db.VarChar(50) // 'preview' | 'technical' | 'technical_archival' | 'graphics'
181
182 // Dual file paths for preview/quality images
183 previewPath String? @db.VarChar(512) // Increased from 255 for SHA256 hash-based paths
184 qualityPath String? @db.VarChar(512) // Increased from 255 for SHA256 hash-based paths
185
186 compressed Boolean @default(false)
187
188 createdBy String? @db.VarChar(150)
189 createdAt DateTime @default(now())
190 updatedBy String? @db.VarChar(150)
191 updatedAt DateTime? @updatedAt
192
193 // Relations
194 document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
195 version Version @relation(fields: [versionId], references: [id], onDelete: Cascade)
196 revision Revision @relation(fields: [revisionId], references: [id], onDelete: Cascade)
197 queue QueueExtractor? @relation(fields: [queueId], references: [id], onDelete: SetNull)
198
199 @@index([documentId])
200 @@index([versionId])
201 @@index([revisionId])
202 @@index([queueId])
203 @@index([documentId, pageOrder])
204 @@index([status])
205 @@index([documentId, versionId, revisionId])
206 @@map("document_pages")
207}
208
209model DocumentComments {
210 id String @id @unique @db.VarChar(50)
211 parentId String? @db.VarChar(50) // For nested comments/replies
212 documentId String @db.VarChar(50)
213 comments String @db.Text
214 editorData String? @db.Text // JSON data from Editor.js
215 editorConfig String? @db.Text // Editor.js configuration
216 createdBy String? @db.VarChar(150)
217 createdAt DateTime @default(now())
218 updatedBy String? @db.VarChar(150)
219 updatedAt DateTime?
220 deletedBy String? @db.VarChar(150)
221 deletedAt DateTime?
222
223 // Relations
224 document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
225 parent DocumentComments? @relation("DocumentCommentReplies", fields: [parentId], references: [id], onDelete: Restrict)
226 replies DocumentComments[] @relation("DocumentCommentReplies")
227 attachments DocumentCommentAttachments[]
228
229 @@index([documentId])
230 @@index([parentId])
231 @@index([createdBy])
232 @@index([deletedAt]) // For soft delete queries
233 @@map("document_comments")
234}
235
236model DocumentCommentAttachments {
237 id String @id @unique @db.VarChar(50)
238 commentId String @db.VarChar(50)
239 fileName String @db.VarChar(255)
240 path String @db.Text
241 extension String @db.VarChar(10)
242 createdBy String? @db.VarChar(150)
243 createdAt DateTime @default(now())
244 updatedBy String? @db.VarChar(150)
245 updatedAt DateTime?
246
247 comment DocumentComments @relation(fields: [commentId], references: [id], onDelete: Cascade)
248
249 @@index([commentId])
250 @@map("document_comment_attachments")
251}
252
253// -------------------------------------
254// 3D Model Metadata (Autodesk APS)
255// -------------------------------------
256model Document3DMetadata {
257 id String @id @unique @db.VarChar(50)
258 documentId String @db.VarChar(50)
259 versionId String @db.VarChar(50)
260 revisionId String @unique @db.VarChar(50)
261
262 s3Key String @db.VarChar(512)
263
264 autodeskObjectId String? @db.Text
265 autodeskUrn String? @db.Text // autodeskObjectId Base64 encoded URN untuk Viewer
266 bucketKey String @db.VarChar(100)
267
268 // Translation / Conversion Status
269 translationStatus AutodeskTranslationStatus @default(PENDING)
270 translationProgress String? @db.VarChar(50)
271 translationErrorMessage String? @db.Text
272
273 createdBy String? @db.VarChar(150) // uuid
274 createdAt DateTime @default(now())
275 updatedBy String? @db.VarChar(150) // uuid
276 updatedAt DateTime? @updatedAt
277
278 document Document @relation(name: "Document3DMetadatas", fields: [documentId], references: [id], onDelete: Cascade)
279 version Version @relation(name: "Version3DMetadatas", fields: [versionId], references: [id], onDelete: Cascade)
280 revision Revision @relation(name: "Revision3DMetadata", fields: [revisionId], references: [id], onDelete: Cascade)
281
282 @@index([documentId])
283 @@index([translationStatus])
284 @@map("document_3d_metadatas")
285}
286
287model Version {
288 id String @id @unique @db.VarChar(50)
289 key String @unique
290 documentId String @db.VarChar(50)
291 folderId String? @db.VarChar(50)
292 version Int @default(0)
293 filePath String @db.VarChar(512) // Increased from 255 for SHA256 hash-based paths
294 fileSize Float
295 fileHash Json? // { value: string, type: 'sha256', calculatedAt: ISO datetime }
296 status VersionStatus?
297 remark String? @db.Text
298 description String? @db.Text
299 companyId String? @db.VarChar(50)
300 inTrash Boolean @default(false)
301 transmittalId String? @db.VarChar(255)
302 transmittalStatus String? @db.VarChar(255)
303 transmittalTimestamp DateTime?
304 publishedRevisionId String? @unique @db.VarChar(50) // Track which revision was published for this version
305
306 archivedBy String? @db.VarChar(150)
307 archivedAt Int? @db.UnsignedInt
308 approvedBy String? @db.VarChar(150)
309 approvedAt Int? @db.UnsignedInt
310 uploadedBy String? @db.VarChar(150)
311 uploadedAt DateTime @default(now())
312 updatedBy String? @db.VarChar(150)
313 updatedAt DateTime?
314
315 folder Folder? @relation(fields: [folderId], references: [id])
316 document Document @relation("DocumentVersions", fields: [documentId], references: [id], onDelete: Cascade)
317 documentActive Document? @relation("DocumentActiveVersion")
318 publishedRevision Revision? @relation("VersionPublishedRevision", fields: [publishedRevisionId], references: [id])
319 revisions Revision[] @relation("VersionRevisions")
320 pages DocumentPages[]
321 document3DMetadatas Document3DMetadata[] @relation("Version3DMetadatas")
322
323 @@index([documentId, folderId, version, companyId, transmittalId])
324 @@map("versions")
325}
326
327model Revision {
328 id String @id @unique @db.VarChar(50)
329 key String @unique
330 documentId String @db.VarChar(50)
331 folderId String? @db.VarChar(50)
332 versionId String @db.VarChar(50) // Reference to the version this revision belongs to
333 revision Int @default(0)
334 filePath String @db.VarChar(512) // Increased from 255 for SHA256 hash-based paths
335 fileSize Float
336 fileHash Json? // { value: string, type: 'sha256', calculatedAt: ISO datetime }
337 status RevisionStatus @default(IN_REVIEW)
338 remark String? @db.Text
339 description String? @db.Text
340 companyId String? @db.VarChar(50)
341 inTrash Boolean @default(false)
342 transmittalId String? @db.VarChar(255)
343 transmittalStatus String? @db.VarChar(255)
344 transmittalTimestamp DateTime?
345
346 archivedBy String? @db.VarChar(150)
347 archivedAt Int? @db.UnsignedInt
348 approvedBy String? @db.VarChar(150)
349 approvedAt Int? @db.UnsignedInt
350 uploadedBy String? @db.VarChar(150)
351 uploadedAt DateTime @default(now())
352 updatedBy String? @db.VarChar(150)
353 updatedAt DateTime?
354
355 folder Folder? @relation(fields: [folderId], references: [id])
356 version Version @relation("VersionRevisions", fields: [versionId], references: [id], onDelete: Cascade)
357 document Document @relation("DocumentRevisions", fields: [documentId], references: [id], onDelete: Cascade)
358 documentActive Document? @relation("DocumentActiveRevision")
359 versionPublished Version? @relation("VersionPublishedRevision")
360 pages DocumentPages[]
361 document3DMetadata Document3DMetadata? @relation("Revision3DMetadata")
362
363 @@index([documentId, folderId, versionId, revision, companyId, transmittalId])
364 @@map("revisions")
365}
366
367model QueueDownload {
368 id String @id @unique @db.VarChar(50)
369 projectId String @db.VarChar(100)
370 companyId String? @db.VarChar(50)
371 directoryId String? @db.VarChar(50) // Made optional for document type
372 modelType DocumentType?
373
374 // Queue type and data for flexible downloads
375 queueType QueueDownloadType @default(DIRECTORY)
376 queueData Json? // Store the flexible data array as JSON
377 archiveName String? @db.VarChar(255) // Store the generated archive name
378
379 status QueueDownloadStatus @default(PENDING)
380 progress Float @default(0) // 0-100 percentage
381
382 // Metadata
383 totalSize BigInt? // Total size in bytes
384 compressedSize BigInt? // Final compressed size in bytes
385 totalDirectories Int? // Total directories count
386 totalDocuments Int? // Total documents count
387
388 // File paths
389 workingPath String? @db.VarChar(500) // Temporary working directory path
390 finalPath String? @db.VarChar(500) // Final compressed file path
391 downloadUrl String? @db.VarChar(500) // Download URL/path
392
393 // Timestamps
394 requestedBy String? @db.VarChar(150)
395 requestedAt DateTime @default(now())
396 startedAt DateTime?
397 completedAt DateTime?
398 expiresAt DateTime? // Auto-expire after 1 day
399
400 // Error handling
401 errorMessage String? @db.Text
402 retryCount Int @default(0)
403
404 @@index([projectId, companyId, status])
405 @@index([status, expiresAt])
406 @@index([queueType])
407 @@map("queue_downloads")
408}
409
410model QueueExtractor {
411 id String @id @unique @db.VarChar(50)
412 projectId String @db.VarChar(100)
413 companyId String? @db.VarChar(50)
414 queueType String? // Null for now as per requirement
415 queueData Json // Store document info: { documentId, versionId, revisionId?, modelType? }
416
417 status QueueExtractorStatus @default(PENDING)
418 progress Float @default(0) // 0-100 percentage
419
420 // Metadata
421 totalSize BigInt? // Total size in bytes
422 compressedSize BigInt? // Final compressed size in bytes (after WebP conversion)
423 totalPages Int @default(0) // Total pages extracted
424
425 // File paths
426 workingPath String? @db.VarChar(500) // Temporary working directory path
427 finalPath String? @db.VarChar(500) // Final output directory path
428
429 // Timestamps
430 requestedBy String? @db.VarChar(150)
431 requestedAt DateTime @default(now())
432 startedAt DateTime?
433 completedAt DateTime?
434
435 // Error handling
436 errorMessage String? @db.Text
437 retryCount Int @default(0)
438
439 // Phase tracking for two-phase extraction
440 queuePhase String? @db.VarChar(20) // 'preview' | 'quality'
441 detectedDpi Int? // Max DPI detected from pdfimages -list
442 detectedType String? @db.VarChar(50) // 'technical' | 'graphics' | 'mixed'
443 selectedPreset String? @db.VarChar(50) // Final preset used for extraction
444
445 // Relations
446 pages DocumentPages[]
447
448 @@index([projectId, companyId, status])
449 @@index([status])
450 @@map("queue_extractors")
451}
452
453model ChangeLog {
454 id String @id @default(cuid())
455 tableName String
456 recordId String
457 operation Operation
458 timestamp DateTime @default(now())
459 processed Boolean @default(false)
460
461 @@map("change_logs")
462}
463
464// -------------------------------------
465// Metrics scope-related models
466// -------------------------------------
467// Metrics for tracking the usage of the system
468model Metrics {
469 id String @id @default(uuid())
470 projectId String @db.VarChar(100)
471 companyId String? @db.VarChar(50)
472
473 totalUsers Int @default(0) @map("total_users")
474 totalDocuments Int @default(0) @map("total_documents")
475 totalDocumentsPublish Int @default(0) @map("total_documents_publish")
476 totalDocumentsType Json @default("{}") @map("total_documents_type")
477 totalDocumentsPublishType Json @default("{}") @map("total_documents_publish_type")
478 totalDocumentsTrashed Int @default(0) @map("total_documents_trashed")
479 totalDocumentsTrashedType Json @default("{}") @map("total_documents_trashed_type")
480 totalDocumentsExtracted Int @default(0) @map("total_documents_extracted")
481 totalStatus Json @default("{}") @map("total_status")
482 totalStatusType Json @default("{}") @map("total_status_type")
483 totalStatusPublish Json @default("{}") @map("total_status_publish")
484 totalStatusPublishType Json @default("{}") @map("total_status_publish_type")
485 totalDirectories Int @default(0) @map("total_directories")
486 totalDirectoriesTrashed Int @default(0) @map("total_directories_trashed")
487 totalVersions Int @default(0) @map("total_versions")
488 totalRevisions Int @default(0) @map("total_revisions")
489 totalDocumentComments Int @default(0) @map("total_doc_comments")
490 totalDocumentCommentReplies Int @default(0) @map("total_doc_comment_replies")
491 totalDocumentCommentAttachments Int @default(0) @map("total_doc_comment_attachments")
492 usedStorage Float @default(0.0) @map("used_storage")
493 usedStorageFormatted String @default("0 B") @map("used_storage_formatted") @db.VarChar(100)
494 usedStorageDetailed Json @default("{}") @map("used_storage_detailed")
495
496 createdAt DateTime @default(now())
497 updatedAt DateTime?
498 deletedAt DateTime?
499
500 @@index([projectId, companyId])
501 @@index([projectId, createdAt])
502 @@index([projectId, updatedAt])
503 @@index([projectId, companyId, deletedAt])
504 @@map("metrics")
505}
506
507// -------------------------------------
508// Audit log scope-related models
509// -------------------------------------
510// Audit logs for security tracking
511model AuditLog {
512 id String @id @default(uuid())
513 user_id String?
514 action String // 'login', 'logout', 'create', 'update', 'delete', 'view'
515 resource String? // table name or resource type
516 resource_id String?
517 old_values Json?
518 new_values Json?
519 ip_address String?
520 user_agent String?
521 location String? // geo location
522 status String @default("success") // 'success', 'failed', 'blocked'
523 created_at DateTime @default(now())
524
525 @@index([user_id])
526 @@index([action])
527 @@index([resource])
528 @@index([resource_id])
529 @@index([status])
530 @@index([created_at])
531 @@map("audit_logs")
532}
533
534// ============================================================================
535// MPP Gantt Cache
536// Menyimpan hasil raw ekstraksi Aspose Tasks Cloud agar tidak perlu
537// memanggil API Aspose setiap kali view Gantt dibuka.
538//
539// Hierarki relasi:
540// MppSnapshot (1) ──< MppTaskItem (tasks.taskItem[])
541// ──< MppTaskDetail (taskDetails[])
542// ──< MppTaskLink (taskLinks[])
543// ──< MppResource (resources.resourceItem[])
544// ──< MppTaskAssignment (taskAssignments[].list[] — di-flatten)
545// ============================================================================
546
547model MppSnapshot {
548 id String @id @default(uuid()) @db.VarChar(50)
549 projectId String @db.VarChar(100)
550 fileName String @db.VarChar(255)
551 /// Path folder di Aspose Cloud Storage (opsional)
552 folder String? @db.VarChar(500)
553 extractedAt DateTime @default(now())
554
555 taskItems MppTaskItem[]
556 taskDetails MppTaskDetail[]
557 taskLinks MppTaskLink[]
558 resources MppResource[]
559 taskAssignments MppTaskAssignment[]
560
561 /// Satu entri cache per kombinasi projectId + fileName
562 @@unique([projectId, fileName], name: "projectId_fileName")
563 @@index([projectId])
564 @@map("mpp_snapshots")
565}
566
567model MppTaskItemLink {
568 id String @id @default(uuid()) @db.VarChar(50)
569 href String
570 rel String
571 type String?
572 title String?
573 mppTaskItems MppTaskItem[]
574
575 @@map("mpp_task_item_links")
576}
577
578/// tasks.taskItem[] — daftar ringkas task (uid, nama, tanggal, durasi)
579model MppTaskItem {
580 id String @id @default(uuid()) @db.VarChar(50)
581 snapshotId String @db.VarChar(50)
582 uid Int
583 /// id (bukan uid) dari Aspose — urutan task di dalam file
584 asposeId Int @map("aspose_id")
585 name String @db.VarChar(500)
586 start DateTime
587 finish DateTime
588 duration String @db.VarChar(50)
589 link MppTaskItemLink @relation(fields: [mppTaskItemLinkId], references: [id])
590
591 snapshot MppSnapshot @relation(fields: [snapshotId], references: [id], onDelete: Cascade)
592 mppTaskItemLinkId String @db.VarChar(50)
593
594 @@unique([snapshotId, uid])
595 @@index([snapshotId])
596 @@map("mpp_task_items")
597}
598
599/// taskDetails[] — detail lengkap per task termasuk hierarki dan prioritas
600model MppTaskDetail {
601 id String @id @default(uuid()) @db.VarChar(50)
602 snapshotId String @db.VarChar(50)
603 uid Int
604 asposeId Int @map("aspose_id")
605 name String @db.VarChar(500)
606 start DateTime
607 finish DateTime
608 duration String @db.VarChar(50)
609 /// 0–100
610 percentComplete Int @default(0) @map("percent_complete")
611 /// Numerik Aspose: 500=Normal, 700=High, 300=Low, dst.
612 priority Int @default(500)
613 isSummary Boolean @default(false) @map("is_summary")
614 isMilestone Boolean @default(false) @map("is_milestone")
615 outlineLevel Int @default(0) @map("outline_level")
616 outlineNumber String? @map("outline_number") @db.VarChar(50)
617 wbs String? @db.VarChar(50)
618 /// Array uid child task, disimpan sebagai JSON int[].
619 /// Kunci untuk invert parent-child di aggregator.
620 subtasksUids Json @map("subtasks_uids")
621 /// Objek Task lengkap dari Aspose — untuk keperluan di masa depan
622 /// tanpa harus memanggil API lagi.
623 rawData Json @map("raw_data")
624
625 snapshot MppSnapshot @relation(fields: [snapshotId], references: [id], onDelete: Cascade)
626
627 @@unique([snapshotId, uid])
628 @@index([snapshotId])
629 @@map("mpp_task_details")
630}
631
632/// taskLinks[] — dependency antar task (FinishToStart, dll.)
633model MppTaskLink {
634 id String @id @default(uuid()) @db.VarChar(50)
635 snapshotId String @db.VarChar(50)
636 /// Index 1-based dari Aspose (dipakai untuk update/delete link)
637 asposeIndex Int @map("aspose_index")
638 predecessorUid Int @map("predecessor_uid")
639 successorUid Int @map("successor_uid")
640 /// "FinishToStart" | "StartToStart" | "FinishToFinish" | "StartToFinish"
641 linkType String @map("link_type") @db.VarChar(50)
642 lag Int @default(0)
643 lagFormat String? @map("lag_format") @db.VarChar(50)
644
645 snapshot MppSnapshot @relation(fields: [snapshotId], references: [id], onDelete: Cascade)
646
647 @@unique([snapshotId, asposeIndex])
648 @@index([snapshotId])
649 @@map("mpp_task_links")
650}
651
652/// resources.resourceItem[] — daftar resource/orang dalam proyek
653model MppResource {
654 id String @id @default(uuid()) @db.VarChar(50)
655 snapshotId String @db.VarChar(50)
656 uid Int
657 asposeId Int @map("aspose_id")
658 /// null untuk resource uid=0 (placeholder "Unassigned" bawaan Aspose)
659 name String? @db.VarChar(500)
660
661 snapshot MppSnapshot @relation(fields: [snapshotId], references: [id], onDelete: Cascade)
662
663 @@unique([snapshotId, uid])
664 @@index([snapshotId])
665 @@map("mpp_resources")
666}
667
668/// taskAssignments[].list[] — flatten dari semua container per task.
669/// Menyimpan pasangan taskUid↔resourceUid untuk resolve PIC di aggregator.
670model MppTaskAssignment {
671 id String @id @default(uuid()) @db.VarChar(50)
672 snapshotId String @db.VarChar(50)
673 /// uid assignment dari Aspose
674 uid Int
675 taskUid Int @map("task_uid")
676 resourceUid Int @map("resource_uid")
677
678 snapshot MppSnapshot @relation(fields: [snapshotId], references: [id], onDelete: Cascade)
679
680 @@unique([snapshotId, uid])
681 @@index([snapshotId])
682 @@index([snapshotId, taskUid])
683 @@map("mpp_task_assignments")
684}