Last active 2 weeks ago

Akihira77's Avatar Akihira77 revised this gist 2 weeks ago. Go to revision

1 file changed, 684 insertions

schema.prisma(file created)

@@ -0,0 +1,684 @@
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 +
7 + generator client {
8 + provider = "prisma-client-js"
9 + previewFeatures = []
10 + }
11 +
12 + datasource db {
13 + provider = "mysql"
14 + url = env("DATABASE_URL")
15 + }
16 +
17 + /**
18 + * Enums
19 + */
20 + // Enums for Document
21 + enum 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 +
35 + enum 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 +
46 + enum RevisionStatus {
47 + IN_REVIEW
48 + APPROVED
49 + APPROVED_WITH_NOTES
50 + NEED_IMPROVEMENT
51 + REJECTED
52 + REJECTED_WITH_NOTES
53 + REJECTED_RESUBMISSION
54 + }
55 +
56 + enum 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
66 + enum QueueDownloadStatus {
67 + PENDING
68 + IN_PROGRESS
69 + COMPLETED
70 + FAILED
71 + EXPIRED
72 + }
73 +
74 + enum QueueDownloadType {
75 + DIRECTORY
76 + DOCUMENT
77 + }
78 +
79 + enum QueueExtractorStatus {
80 + PENDING
81 + IN_PROGRESS
82 + COMPLETED
83 + FAILED
84 + EXPIRED
85 + }
86 +
87 + // Enums for Change Log (CDC)
88 + enum Operation {
89 + CREATE
90 + UPDATE
91 + DELETE
92 + }
93 +
94 + enum AutodeskTranslationStatus {
95 + PENDING
96 + IN_PROGRESS
97 + SUCCESS
98 + FAILED
99 + }
100 +
101 + /**
102 + * Models
103 + */
104 + model 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 +
127 + model 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 +
157 + model 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 +
209 + model 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 +
236 + model 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 + // -------------------------------------
256 + model 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 +
287 + model 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 +
327 + model 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 +
367 + model 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 +
410 + model 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 +
453 + model 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
468 + model 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
511 + model 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 +
547 + model 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 +
567 + model 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)
579 + model 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
600 + model 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.)
633 + model 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
653 + model 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.
670 + model 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 + }
Newer Older