@adaptware/eagles-db 0.5.33 → 0.5.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/edge.js +231 -11
- package/client/index-browser.js +225 -4
- package/client/index.d.ts +67169 -46191
- package/client/index.js +231 -11
- package/client/libquery_engine-darwin.dylib.node +0 -0
- package/client/package.json +1 -1
- package/client/schema.prisma +454 -105
- package/client/wasm.js +231 -11
- package/package.json +1 -1
- package/prisma/.DS_Store +0 -0
- package/prisma/migrations/20260519120000_migrate_shortlisted_to_selected/migration.sql +2 -0
- package/prisma/schema/.DS_Store +0 -0
- package/prisma/schema/department.prisma +3 -2
- package/prisma/schema/jobDescription.prisma +1 -0
- package/prisma/schema/licensing/aiCostLedger.prisma +34 -0
- package/prisma/schema/licensing/assignmentHistory.prisma +19 -0
- package/prisma/schema/licensing/licensePackageCatalog.prisma +62 -0
- package/prisma/schema/licensing/licensingEnums.prisma +80 -0
- package/prisma/schema/licensing/licensingLedger.prisma +32 -0
- package/prisma/schema/licensing/purchasedLicense.prisma +74 -0
- package/prisma/schema/licensing/purchasedTopUp.prisma +39 -0
- package/prisma/schema/migrations/20260519120000_add_analytics_schema/migration.sql +84 -0
- package/prisma/schema/migrations/20260625120000_add_interviewer_analytics/migration.sql +26 -0
- package/prisma/schema/migrations/20260629120000_add_user_lifecycle_activity_events/migration.sql +13 -0
- package/prisma/schema/migrations/20260629140000_drop_organisation_org_code/migration.sql +2 -0
- package/prisma/schema/migrations/20260629160000_employee_codes_dual_interviewer_analytics/migration.sql +87 -0
- package/prisma/schema/migrations/20260630120000_add_application_sources/migration.sql +38 -0
- package/prisma/schema/migrations/20260701120000_add_source_effectiveness_analytics/migration.sql +40 -0
- package/prisma/schema/migrations/20260702120000_add_analytics_daily_tables/migration.sql +232 -0
- package/prisma/schema/migrations/20260716120000_evaluation_category_four_values/migration.sql +204 -0
- package/prisma/schema/migrations/20260716130000_application_withdrawn_analytics/migration.sql +31 -0
- package/prisma/schema/migrations/20260804120000_contact_us_optional_website_subject/migration.sql +3 -0
- package/prisma/schema/migrations/20260810120000_add_licensing_schema/migration.sql +237 -0
- package/prisma/schema/migrations/20260814153000_add_licensing_ledger/migration.sql +236 -0
- package/prisma/schema/migrations/20260828120000_license_package_orgs_cost_price_ai_cost_ledger/migration.sql +74 -0
- package/prisma/schema/migrations/20260831120000_ai_cost_ledger_purchase_reason/migration.sql +2 -0
- package/prisma/schema/migrations/20260831140000_ai_cost_ledger_nullable_hiring_unit/migration.sql +3 -0
- package/prisma/schema/organisation.prisma +6 -0
- package/prisma/schema/schema.prisma +1 -1
- package/prisma/schema/migrations/20260522120000_ongoing_evaluation_record_kind/migration.sql +0 -25
- package/prisma/schema/migrations/20260523120000_replace_record_kind_with_is_committed/migration.sql +0 -24
- package/prisma/schema/migrations/20260528120000_drop_job_description_recommendation_config/migration.sql +0 -2
- package/prisma/schema/migrations/20260710120000_add_resume_rendered_export_fields/migration.sql +0 -9
- package/prisma/schema/migrations/20260714120000_add_job_description_display_html_document/migration.sql +0 -8
- package/prisma/schema/migrations/20260716120000_add_offer_letters/migration.sql +0 -183
- package/prisma/schema/migrations/20260716140000_offer_schema_refinements/migration.sql +0 -13
- package/prisma/schema/migrations/20260716160000_offer_schema_hardening/migration.sql +0 -95
- package/prisma/schema/migrations/20260716180000_offer_letter_template_config/migration.sql +0 -2
- package/prisma/schema/migrations/20260716190000_add_evaluation_focus_areas/migration.sql +0 -6
- package/prisma/schema/migrations/20260717120000_offer_workflow_approval/migration.sql +0 -30
- package/prisma/schema/migrations/20260720120000_offer_letter_template_is_draft/migration.sql +0 -5
- package/prisma/schema/migrations/20260720140000_offer_status_split/migration.sql +0 -51
- package/prisma/schema/migrations/20260720140001_offer_status_split_backfill/migration.sql +0 -37
- package/prisma/schema/migrations/20260722120000_offer_draft_edited_html/migration.sql +0 -13
- package/prisma/schema/migrations/20260723150000_offer_withdraw_actor_no_show_reason/migration.sql +0 -10
- package/prisma/schema/migrations/20260724120000_open_job_offer_accepted_declined_counts/migration.sql +0 -7
- package/prisma/schema/migrations/20260811120000_add_integration_provider_razorpay/migration.sql +0 -6
- package/prisma/schema/migrations/20260812120000_add_billing_transactions/migration.sql +0 -49
- package/prisma/schema/migrations/20260812130000_drop_billing_transaction_gst_snapshots/migration.sql +0 -3
- package/prisma/schema/migrations/20260812180000_billing_transaction_invoice_number/migration.sql +0 -5
- package/prisma/schema/migrations/20260813120000_payment_webhooks_gst_refunds/migration.sql +0 -79
- package/prisma/schema/migrations/20260813140000_platform_gst_profiles/migration.sql +0 -49
- package/prisma/schema/migrations/20260813150000_rename_gst_profiles/migration.sql +0 -53
- package/prisma/schema/migrations/20260813160000_remove_gst_billing/migration.sql +0 -16
- package/prisma/schema/migrations/20260813170000_billing_licensing_unrecoverable/migration.sql +0 -2
- package/prisma/schema/migrations/20260813180000_billing_transaction_payment_method/migration.sql +0 -2
- package/prisma/schema/migrations/20260814090000_billing_transaction_purchaser_email/migration.sql +0 -2
- package/prisma/schema/migrations/20260816100000_billing_product_organisations/migration.sql +0 -35
- package/prisma/schema/migrations/20260816120000_merge_billing_product_organisations/migration.sql +0 -25
- package/prisma/schema/migrations/20260817120000_billing_payment_mismatch/migration.sql +0 -7
- package/prisma/schema/migrations/20260819120000_billing_product_prices/migration.sql +0 -62
- package/prisma/schema/migrations/20260819140000_billing_exchange_rates/migration.sql +0 -25
- package/prisma/schema/migrations/20260820120000_billing_transaction_failure_category/migration.sql +0 -31
- package/prisma/schema/migrations/20260820130000_backfill_billing_transaction_failure_details/migration.sql +0 -23
- package/prisma/schema/migrations/20260820140000_drop_billing_product_price_overrides/migration.sql +0 -2
- package/prisma/schema/migrations/20260821120000_drop_billing_product_and_mock_licensing/migration.sql +0 -38
- package/prisma/schema/migrations/20260824120000_billing_transaction_purchase_display_name/migration.sql +0 -2
- package/prisma/schema/migrations/20260831120000_user_communication_email/migration.sql +0 -6
package/package.json
CHANGED
package/prisma/.DS_Store
ADDED
|
Binary file
|
|
Binary file
|
|
@@ -38,8 +38,9 @@ model Department {
|
|
|
38
38
|
hiring_plan_lines HiringPlanLine[]
|
|
39
39
|
employees Employee[]
|
|
40
40
|
job_descriptions JobDescription[]
|
|
41
|
-
job_profiles
|
|
42
|
-
sections
|
|
41
|
+
job_profiles JobProfile[]
|
|
42
|
+
sections HiringPlanSection[]
|
|
43
|
+
assigned_hiring_units PurchasedHiringUnit[]
|
|
43
44
|
|
|
44
45
|
@@unique([organisation_id, name])
|
|
45
46
|
@@index([organisation_id])
|
|
@@ -72,6 +72,7 @@ model JobDescription {
|
|
|
72
72
|
hiring_plan_line HiringPlanLine? @relation("PlanLineRequisitions", fields: [hiring_plan_line_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
|
73
73
|
/// Plan lines that use this JD as the role template/reference.
|
|
74
74
|
referenced_by_plan_lines HiringPlanLine[] @relation("PlanLineTemplateJd")
|
|
75
|
+
linked_hiring_units PurchasedHiringUnit[]
|
|
75
76
|
|
|
76
77
|
@@index([organisation_id])
|
|
77
78
|
@@index([hiring_manager_id])
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/// Append-only book of AI workflow cost (org pool, optionally a hiring unit).
|
|
2
|
+
/// Covers every workflow (including license-included names). Amount is always USD.
|
|
3
|
+
/// total_cost(unit) = SUM(DEBIT amount) - SUM(CREDIT amount) for that unit.
|
|
4
|
+
/// CREDIT / PURCHASE is full package internal cost (USD) per hiring unit.
|
|
5
|
+
/// DEBIT / USAGE is every workflow's Orion totalCost (USD), nothing excluded.
|
|
6
|
+
model AiCostLedgerEntry {
|
|
7
|
+
id String @id @default(uuid())
|
|
8
|
+
organisation_id String
|
|
9
|
+
purchased_hiring_unit_id String?
|
|
10
|
+
workflow_name String
|
|
11
|
+
direction LicensingLedgerDirection
|
|
12
|
+
/// Always non-negative. USD.
|
|
13
|
+
amount Decimal @db.Decimal(20, 4)
|
|
14
|
+
reason AiCostLedgerReason
|
|
15
|
+
occurred_at DateTime @default(now())
|
|
16
|
+
source_type String?
|
|
17
|
+
source_id String?
|
|
18
|
+
description String?
|
|
19
|
+
idempotency_key String @unique
|
|
20
|
+
created_at DateTime @default(now())
|
|
21
|
+
updated_at DateTime @default(now()) @updatedAt
|
|
22
|
+
created_by String
|
|
23
|
+
updated_by String
|
|
24
|
+
is_active Boolean @default(true)
|
|
25
|
+
|
|
26
|
+
organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
|
|
27
|
+
purchased_hiring_unit PurchasedHiringUnit? @relation(fields: [purchased_hiring_unit_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
|
|
28
|
+
|
|
29
|
+
@@index([purchased_hiring_unit_id, occurred_at], map: "ai_cost_ledger_unit_occurred_idx")
|
|
30
|
+
@@index([purchased_hiring_unit_id, workflow_name, occurred_at], map: "ai_cost_ledger_unit_workflow_occurred_idx")
|
|
31
|
+
@@index([organisation_id, occurred_at], map: "ai_cost_ledger_org_occurred_idx")
|
|
32
|
+
@@map("ai_cost_ledger")
|
|
33
|
+
@@schema("licensing")
|
|
34
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/// Immutable assignment history for Hiring Unit department/job changes.
|
|
2
|
+
model PurchasedHiringUnitAssignmentEvent {
|
|
3
|
+
id String @id @default(uuid())
|
|
4
|
+
purchased_hiring_unit_id String
|
|
5
|
+
event_kind PurchasedHiringUnitAssignmentEventKind
|
|
6
|
+
previous_department_id String?
|
|
7
|
+
new_department_id String?
|
|
8
|
+
previous_job_description_id String?
|
|
9
|
+
new_job_description_id String?
|
|
10
|
+
effective_at DateTime @default(now())
|
|
11
|
+
created_by String
|
|
12
|
+
|
|
13
|
+
purchased_hiring_unit PurchasedHiringUnit @relation(fields: [purchased_hiring_unit_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
|
|
14
|
+
|
|
15
|
+
@@index([purchased_hiring_unit_id], map: "purchased_hu_assignment_events_unit_id_idx")
|
|
16
|
+
@@index([purchased_hiring_unit_id, effective_at], map: "purchased_hu_assignment_events_unit_effective_idx")
|
|
17
|
+
@@map("purchased_hiring_unit_assignment_events")
|
|
18
|
+
@@schema("licensing")
|
|
19
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/// Product catalogue: one wide row per (code, version) with fixed per-hiring-unit entitlements.
|
|
2
|
+
/// See eagles-db/docs/eagles-db/licensing-catalog.md for column → usage metric_key mapping at purchase.
|
|
3
|
+
model LicensePackage {
|
|
4
|
+
id String @id @default(uuid())
|
|
5
|
+
code String
|
|
6
|
+
version Int
|
|
7
|
+
name String
|
|
8
|
+
description String?
|
|
9
|
+
price Decimal @db.Decimal(18, 2)
|
|
10
|
+
/// Internal cost of the package. Same currency as `currency`. Not for org-facing catalogue APIs.
|
|
11
|
+
cost_price Decimal @default(0) @db.Decimal(18, 2)
|
|
12
|
+
currency String @default("INR")
|
|
13
|
+
status LicensePackageStatus @default(DRAFT)
|
|
14
|
+
/// Number of PurchasedHiringUnit rows created per package purchase.
|
|
15
|
+
units_per_purchase Int
|
|
16
|
+
|
|
17
|
+
/// Per-hiring-unit entitlements. null = not included; 0 = explicitly zero.
|
|
18
|
+
interview_ai_assisted_hours Decimal? @db.Decimal(20, 4)
|
|
19
|
+
interview_full_ai_hours Decimal? @db.Decimal(20, 4)
|
|
20
|
+
interview_non_ai_hours Decimal? @db.Decimal(20, 4)
|
|
21
|
+
assignment_count Decimal? @db.Decimal(20, 4)
|
|
22
|
+
test_count Decimal? @db.Decimal(20, 4)
|
|
23
|
+
resume_count Decimal? @db.Decimal(20, 4)
|
|
24
|
+
credits Decimal? @db.Decimal(20, 4)
|
|
25
|
+
|
|
26
|
+
created_at DateTime @default(now())
|
|
27
|
+
updated_at DateTime @updatedAt
|
|
28
|
+
created_by String
|
|
29
|
+
updated_by String
|
|
30
|
+
is_active Boolean @default(true)
|
|
31
|
+
|
|
32
|
+
purchased_license_packages PurchasedLicensePackage[]
|
|
33
|
+
organisations LicensePackageOrganisation[]
|
|
34
|
+
|
|
35
|
+
@@unique([code, version])
|
|
36
|
+
@@index([code])
|
|
37
|
+
@@index([status])
|
|
38
|
+
@@map("license_packages")
|
|
39
|
+
@@schema("licensing")
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// Many-to-many: which organisations may see this catalogue package.
|
|
43
|
+
/// Zero rows = visible to nobody except platform list (enforced later in eagles-backend).
|
|
44
|
+
model LicensePackageOrganisation {
|
|
45
|
+
id String @id @default(uuid())
|
|
46
|
+
license_package_id String
|
|
47
|
+
organisation_id String
|
|
48
|
+
|
|
49
|
+
created_at DateTime @default(now())
|
|
50
|
+
updated_at DateTime @updatedAt
|
|
51
|
+
created_by String
|
|
52
|
+
updated_by String
|
|
53
|
+
is_active Boolean @default(true)
|
|
54
|
+
|
|
55
|
+
license_package LicensePackage @relation(fields: [license_package_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
|
|
56
|
+
organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
|
|
57
|
+
|
|
58
|
+
@@unique([license_package_id, organisation_id], map: "license_package_orgs_pkg_org_key")
|
|
59
|
+
@@index([organisation_id])
|
|
60
|
+
@@map("license_package_organisations")
|
|
61
|
+
@@schema("licensing")
|
|
62
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
enum LicensePackageStatus {
|
|
2
|
+
DRAFT
|
|
3
|
+
ACTIVE
|
|
4
|
+
ARCHIVED
|
|
5
|
+
|
|
6
|
+
@@schema("licensing")
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
enum PurchasedLicensePackageStatus {
|
|
10
|
+
ACTIVE
|
|
11
|
+
EXPIRED
|
|
12
|
+
CANCELLED
|
|
13
|
+
|
|
14
|
+
@@schema("licensing")
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
enum PurchasedHiringUnitStatus {
|
|
18
|
+
AVAILABLE
|
|
19
|
+
ASSIGNED
|
|
20
|
+
ACTIVE
|
|
21
|
+
EXPIRED
|
|
22
|
+
|
|
23
|
+
@@schema("licensing")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
enum PurchasedTopUpStatus {
|
|
27
|
+
ACTIVE
|
|
28
|
+
DEPLETED
|
|
29
|
+
CANCELLED
|
|
30
|
+
|
|
31
|
+
@@schema("licensing")
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
enum PurchasedHiringUnitAssignmentEventKind {
|
|
35
|
+
DEPARTMENT_ASSIGNED
|
|
36
|
+
DEPARTMENT_UNASSIGNED
|
|
37
|
+
JOB_LINKED
|
|
38
|
+
JOB_UNLINKED
|
|
39
|
+
|
|
40
|
+
@@schema("licensing")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
enum LicensingLedgerEntitlementType {
|
|
44
|
+
INTERVIEW_AI_ASSISTED_HOURS
|
|
45
|
+
INTERVIEW_FULL_AI_HOURS
|
|
46
|
+
INTERVIEW_NON_AI_HOURS
|
|
47
|
+
ASSIGNMENT_COUNT
|
|
48
|
+
TEST_COUNT
|
|
49
|
+
RESUME_COUNT
|
|
50
|
+
CREDITS
|
|
51
|
+
|
|
52
|
+
@@schema("licensing")
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
enum LicensingLedgerDirection {
|
|
56
|
+
CREDIT
|
|
57
|
+
DEBIT
|
|
58
|
+
|
|
59
|
+
@@schema("licensing")
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
enum LicensingLedgerReason {
|
|
63
|
+
PURCHASE
|
|
64
|
+
TOP_UP
|
|
65
|
+
USAGE
|
|
66
|
+
ADJUSTMENT
|
|
67
|
+
REFUND
|
|
68
|
+
EXPIRY
|
|
69
|
+
|
|
70
|
+
@@schema("licensing")
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
enum AiCostLedgerReason {
|
|
74
|
+
PURCHASE
|
|
75
|
+
USAGE
|
|
76
|
+
ADJUSTMENT
|
|
77
|
+
REFUND
|
|
78
|
+
|
|
79
|
+
@@schema("licensing")
|
|
80
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/// Append-only book of hiring-unit entitlement movements.
|
|
2
|
+
/// remaining(unit, type) = SUM(CREDIT amount) - SUM(DEBIT amount).
|
|
3
|
+
/// See eagles-db/docs/eagles-db/licensing-hiring-unit.md.
|
|
4
|
+
model LicensingLedgerEntry {
|
|
5
|
+
id String @id @default(uuid())
|
|
6
|
+
organisation_id String
|
|
7
|
+
/// Null for org-level rows (e.g. unallocated top-ups later).
|
|
8
|
+
purchased_hiring_unit_id String?
|
|
9
|
+
entitlement_type LicensingLedgerEntitlementType
|
|
10
|
+
direction LicensingLedgerDirection
|
|
11
|
+
/// Always non-negative. CREDIT of 0 means the stem is included at zero.
|
|
12
|
+
amount Decimal @db.Decimal(20, 4)
|
|
13
|
+
reason LicensingLedgerReason
|
|
14
|
+
occurred_at DateTime @default(now())
|
|
15
|
+
source_type String?
|
|
16
|
+
source_id String?
|
|
17
|
+
description String?
|
|
18
|
+
idempotency_key String @unique
|
|
19
|
+
created_at DateTime @default(now())
|
|
20
|
+
updated_at DateTime @default(now()) @updatedAt
|
|
21
|
+
created_by String
|
|
22
|
+
updated_by String
|
|
23
|
+
is_active Boolean @default(true)
|
|
24
|
+
|
|
25
|
+
organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
|
|
26
|
+
purchased_hiring_unit PurchasedHiringUnit? @relation(fields: [purchased_hiring_unit_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
|
|
27
|
+
|
|
28
|
+
@@index([purchased_hiring_unit_id, entitlement_type, occurred_at], map: "licensing_ledger_unit_type_occurred_idx")
|
|
29
|
+
@@index([organisation_id, entitlement_type, occurred_at], map: "licensing_ledger_org_type_occurred_idx")
|
|
30
|
+
@@map("licensing_ledger")
|
|
31
|
+
@@schema("licensing")
|
|
32
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/// Historical package purchase by an organisation (independent per transaction).
|
|
2
|
+
model PurchasedLicensePackage {
|
|
3
|
+
id String @id @default(uuid())
|
|
4
|
+
organisation_id String
|
|
5
|
+
license_package_id String
|
|
6
|
+
/// Snapshot of catalogue identity at purchase time.
|
|
7
|
+
package_code String
|
|
8
|
+
package_version Int
|
|
9
|
+
package_name String
|
|
10
|
+
price Decimal @db.Decimal(18, 2)
|
|
11
|
+
currency String @default("INR")
|
|
12
|
+
units_per_purchase Int
|
|
13
|
+
/// How many package instances were bought in this transaction.
|
|
14
|
+
package_quantity Int @default(1)
|
|
15
|
+
purchased_at DateTime @default(now())
|
|
16
|
+
status PurchasedLicensePackageStatus @default(ACTIVE)
|
|
17
|
+
created_at DateTime @default(now())
|
|
18
|
+
updated_at DateTime @updatedAt
|
|
19
|
+
created_by String
|
|
20
|
+
updated_by String
|
|
21
|
+
is_active Boolean @default(true)
|
|
22
|
+
|
|
23
|
+
organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
|
|
24
|
+
license_package LicensePackage @relation(fields: [license_package_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
|
|
25
|
+
hiring_units PurchasedHiringUnit[]
|
|
26
|
+
|
|
27
|
+
@@index([organisation_id])
|
|
28
|
+
@@index([organisation_id, status])
|
|
29
|
+
@@index([organisation_id, purchased_at])
|
|
30
|
+
@@map("purchased_license_packages")
|
|
31
|
+
@@schema("licensing")
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// Runtime hiring unit identity and allocation. Entitlement balances live on
|
|
35
|
+
/// `licensing.licensing_ledger` (Prisma model `LicensingLedgerEntry`).
|
|
36
|
+
/// See eagles-db/docs/eagles-db/licensing-hiring-unit.md.
|
|
37
|
+
model PurchasedHiringUnit {
|
|
38
|
+
id String @id @default(uuid())
|
|
39
|
+
organisation_id String
|
|
40
|
+
purchased_license_package_id String
|
|
41
|
+
status PurchasedHiringUnitStatus @default(AVAILABLE)
|
|
42
|
+
/// Current department allocation (licensing allocation, not hiring manager).
|
|
43
|
+
department_id String?
|
|
44
|
+
/// Set once when department_id is first assigned; not cleared on reassignment.
|
|
45
|
+
first_assigned_at DateTime?
|
|
46
|
+
/// Optional unit expiry.
|
|
47
|
+
expiry_at DateTime?
|
|
48
|
+
/// Optional job association (distinct from JobDescription.hiring_manager_id).
|
|
49
|
+
job_description_id String?
|
|
50
|
+
|
|
51
|
+
created_at DateTime @default(now())
|
|
52
|
+
updated_at DateTime @updatedAt
|
|
53
|
+
created_by String
|
|
54
|
+
updated_by String
|
|
55
|
+
is_active Boolean @default(true)
|
|
56
|
+
|
|
57
|
+
organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
|
|
58
|
+
purchased_license_package PurchasedLicensePackage @relation(fields: [purchased_license_package_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
|
|
59
|
+
department Department? @relation(fields: [department_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
|
60
|
+
job_description JobDescription? @relation(fields: [job_description_id], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
|
61
|
+
assignment_events PurchasedHiringUnitAssignmentEvent[]
|
|
62
|
+
ledger_entries LicensingLedgerEntry[]
|
|
63
|
+
ai_cost_ledger_entries AiCostLedgerEntry[]
|
|
64
|
+
|
|
65
|
+
@@index([organisation_id])
|
|
66
|
+
@@index([purchased_license_package_id])
|
|
67
|
+
@@index([department_id])
|
|
68
|
+
@@index([job_description_id])
|
|
69
|
+
@@index([organisation_id, status])
|
|
70
|
+
@@index([organisation_id, department_id])
|
|
71
|
+
@@index([organisation_id, job_description_id])
|
|
72
|
+
@@map("purchased_hiring_units")
|
|
73
|
+
@@schema("licensing")
|
|
74
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/// Wide top-up purchase: org-level per-metric pool (purchased / remaining) before allocation to hiring units.
|
|
2
|
+
model PurchasedTopUp {
|
|
3
|
+
id String @id @default(uuid())
|
|
4
|
+
organisation_id String
|
|
5
|
+
purchased_at DateTime @default(now())
|
|
6
|
+
status PurchasedTopUpStatus @default(ACTIVE)
|
|
7
|
+
total_price Decimal? @db.Decimal(18, 2)
|
|
8
|
+
currency String? @default("INR")
|
|
9
|
+
description String?
|
|
10
|
+
|
|
11
|
+
interview_ai_assisted_hours_purchased Decimal? @db.Decimal(20, 4)
|
|
12
|
+
interview_full_ai_hours_purchased Decimal? @db.Decimal(20, 4)
|
|
13
|
+
interview_non_ai_hours_purchased Decimal? @db.Decimal(20, 4)
|
|
14
|
+
assignment_count_purchased Decimal? @db.Decimal(20, 4)
|
|
15
|
+
test_count_purchased Decimal? @db.Decimal(20, 4)
|
|
16
|
+
resume_count_purchased Decimal? @db.Decimal(20, 4)
|
|
17
|
+
credits_purchased Decimal? @db.Decimal(20, 4)
|
|
18
|
+
|
|
19
|
+
interview_ai_assisted_hours_remaining Decimal? @db.Decimal(20, 4)
|
|
20
|
+
interview_full_ai_hours_remaining Decimal? @db.Decimal(20, 4)
|
|
21
|
+
interview_non_ai_hours_remaining Decimal? @db.Decimal(20, 4)
|
|
22
|
+
assignment_count_remaining Decimal? @db.Decimal(20, 4)
|
|
23
|
+
test_count_remaining Decimal? @db.Decimal(20, 4)
|
|
24
|
+
resume_count_remaining Decimal? @db.Decimal(20, 4)
|
|
25
|
+
credits_remaining Decimal? @db.Decimal(20, 4)
|
|
26
|
+
|
|
27
|
+
created_at DateTime @default(now())
|
|
28
|
+
updated_at DateTime @updatedAt
|
|
29
|
+
created_by String
|
|
30
|
+
updated_by String
|
|
31
|
+
is_active Boolean @default(true)
|
|
32
|
+
|
|
33
|
+
organisation Organisation @relation(fields: [organisation_id], references: [id], onDelete: Restrict, onUpdate: Cascade)
|
|
34
|
+
|
|
35
|
+
@@index([organisation_id])
|
|
36
|
+
@@index([organisation_id, status])
|
|
37
|
+
@@map("purchased_top_ups")
|
|
38
|
+
@@schema("licensing")
|
|
39
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
-- CreateSchema
|
|
2
|
+
CREATE SCHEMA IF NOT EXISTS "analytics";
|
|
3
|
+
|
|
4
|
+
-- CreateEnum
|
|
5
|
+
CREATE TYPE "analytics"."RoundResult" AS ENUM ('PASSED', 'FAILED');
|
|
6
|
+
|
|
7
|
+
-- CreateTable
|
|
8
|
+
CREATE TABLE "analytics"."round_analytics" (
|
|
9
|
+
"id" TEXT NOT NULL,
|
|
10
|
+
"organisation_id" TEXT NOT NULL,
|
|
11
|
+
"application_id" TEXT NOT NULL,
|
|
12
|
+
"candidate_id" TEXT NOT NULL,
|
|
13
|
+
"job_description_id" TEXT NOT NULL,
|
|
14
|
+
"evaluation_plan_id" TEXT NOT NULL,
|
|
15
|
+
"round_id" TEXT NOT NULL,
|
|
16
|
+
"round_name" TEXT,
|
|
17
|
+
"evaluation_category" "EvaluationCategory",
|
|
18
|
+
"round_type" "RoundType",
|
|
19
|
+
"assigned_at" TIMESTAMP(3),
|
|
20
|
+
"interview_created_at" TIMESTAMP(3),
|
|
21
|
+
"interview_scheduled_at" TIMESTAMP(3),
|
|
22
|
+
"interview_started_at" TIMESTAMP(3),
|
|
23
|
+
"interview_completed_at" TIMESTAMP(3),
|
|
24
|
+
"interview_evaluated_at" TIMESTAMP(3),
|
|
25
|
+
"round_completed_at" TIMESTAMP(3),
|
|
26
|
+
"round_result" "analytics"."RoundResult",
|
|
27
|
+
"round_result_at" TIMESTAMP(3),
|
|
28
|
+
"scheduling_duration_minutes" INTEGER,
|
|
29
|
+
"interview_duration_minutes" INTEGER,
|
|
30
|
+
"feedback_duration_minutes" INTEGER,
|
|
31
|
+
"decision_duration_minutes" INTEGER,
|
|
32
|
+
"total_round_duration_minutes" INTEGER,
|
|
33
|
+
"is_custom" BOOLEAN NOT NULL DEFAULT false,
|
|
34
|
+
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
35
|
+
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
36
|
+
|
|
37
|
+
CONSTRAINT "round_analytics_pkey" PRIMARY KEY ("id")
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
-- CreateTable
|
|
41
|
+
CREATE TABLE "analytics"."job_round_analytics" (
|
|
42
|
+
"id" TEXT NOT NULL,
|
|
43
|
+
"organisation_id" TEXT NOT NULL,
|
|
44
|
+
"job_description_id" TEXT NOT NULL,
|
|
45
|
+
"evaluation_plan_id" TEXT NOT NULL,
|
|
46
|
+
"round_name" TEXT NOT NULL,
|
|
47
|
+
"evaluation_category" "EvaluationCategory",
|
|
48
|
+
"is_custom" BOOLEAN NOT NULL DEFAULT false,
|
|
49
|
+
"assigned_count" INTEGER NOT NULL DEFAULT 0,
|
|
50
|
+
"completed_count" INTEGER NOT NULL DEFAULT 0,
|
|
51
|
+
"passed_count" INTEGER NOT NULL DEFAULT 0,
|
|
52
|
+
"failed_count" INTEGER NOT NULL DEFAULT 0,
|
|
53
|
+
"withdrawn_count" INTEGER NOT NULL DEFAULT 0,
|
|
54
|
+
"no_show_count" INTEGER NOT NULL DEFAULT 0,
|
|
55
|
+
"total_completion_minutes" BIGINT NOT NULL DEFAULT 0,
|
|
56
|
+
"avg_completion_minutes" DOUBLE PRECISION,
|
|
57
|
+
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
58
|
+
|
|
59
|
+
CONSTRAINT "job_round_analytics_pkey" PRIMARY KEY ("id")
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
-- CreateIndex
|
|
63
|
+
CREATE UNIQUE INDEX "round_analytics_round_id_key" ON "analytics"."round_analytics"("round_id");
|
|
64
|
+
|
|
65
|
+
-- CreateIndex
|
|
66
|
+
CREATE INDEX "round_analytics_organisation_id_idx" ON "analytics"."round_analytics"("organisation_id");
|
|
67
|
+
|
|
68
|
+
-- CreateIndex
|
|
69
|
+
CREATE INDEX "round_analytics_job_description_id_idx" ON "analytics"."round_analytics"("job_description_id");
|
|
70
|
+
|
|
71
|
+
-- CreateIndex
|
|
72
|
+
CREATE INDEX "round_analytics_application_id_idx" ON "analytics"."round_analytics"("application_id");
|
|
73
|
+
|
|
74
|
+
-- CreateIndex
|
|
75
|
+
CREATE INDEX "round_analytics_candidate_id_idx" ON "analytics"."round_analytics"("candidate_id");
|
|
76
|
+
|
|
77
|
+
-- CreateIndex
|
|
78
|
+
CREATE INDEX "round_analytics_evaluation_category_idx" ON "analytics"."round_analytics"("evaluation_category");
|
|
79
|
+
|
|
80
|
+
-- CreateIndex
|
|
81
|
+
CREATE INDEX "round_analytics_round_result_idx" ON "analytics"."round_analytics"("round_result");
|
|
82
|
+
|
|
83
|
+
-- CreateIndex
|
|
84
|
+
CREATE UNIQUE INDEX "job_round_analytics_job_description_id_evaluation_plan_id_key" ON "analytics"."job_round_analytics"("job_description_id", "evaluation_plan_id");
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
CREATE TABLE "analytics"."interviewer_analytics" (
|
|
3
|
+
"id" TEXT NOT NULL,
|
|
4
|
+
"organisation_id" TEXT NOT NULL,
|
|
5
|
+
"user_id" TEXT NOT NULL,
|
|
6
|
+
"name" TEXT,
|
|
7
|
+
"employee_id" TEXT,
|
|
8
|
+
"interview_count" INTEGER NOT NULL DEFAULT 0,
|
|
9
|
+
"accepted_count" INTEGER NOT NULL DEFAULT 0,
|
|
10
|
+
"rejected_count" INTEGER NOT NULL DEFAULT 0,
|
|
11
|
+
"unsure_count" INTEGER NOT NULL DEFAULT 0,
|
|
12
|
+
"ai_disagreement_count" INTEGER NOT NULL DEFAULT 0,
|
|
13
|
+
"ai_disagreement_percent" DOUBLE PRECISION,
|
|
14
|
+
"avg_difficulty" DOUBLE PRECISION,
|
|
15
|
+
"avg_supportiveness" DOUBLE PRECISION,
|
|
16
|
+
"avg_topics_coverage_percent" DOUBLE PRECISION,
|
|
17
|
+
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
18
|
+
|
|
19
|
+
CONSTRAINT "interviewer_analytics_pkey" PRIMARY KEY ("id")
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
-- CreateIndex
|
|
23
|
+
CREATE UNIQUE INDEX "interviewer_analytics_organisation_id_user_id_key" ON "analytics"."interviewer_analytics"("organisation_id", "user_id");
|
|
24
|
+
|
|
25
|
+
-- CreateIndex
|
|
26
|
+
CREATE INDEX "interviewer_analytics_organisation_id_idx" ON "analytics"."interviewer_analytics"("organisation_id");
|
package/prisma/schema/migrations/20260629120000_add_user_lifecycle_activity_events/migration.sql
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- AlterEnum
|
|
2
|
+
ALTER TYPE "public"."ActivityEventType" ADD VALUE 'USER_INVITED';
|
|
3
|
+
ALTER TYPE "public"."ActivityEventType" ADD VALUE 'USER_INVITATION_RESENT';
|
|
4
|
+
ALTER TYPE "public"."ActivityEventType" ADD VALUE 'USER_CREATED';
|
|
5
|
+
ALTER TYPE "public"."ActivityEventType" ADD VALUE 'USER_INVITATION_ACCEPTED';
|
|
6
|
+
ALTER TYPE "public"."ActivityEventType" ADD VALUE 'USER_INVITATION_EXPIRED';
|
|
7
|
+
ALTER TYPE "public"."ActivityEventType" ADD VALUE 'USER_DEACTIVATED';
|
|
8
|
+
ALTER TYPE "public"."ActivityEventType" ADD VALUE 'USER_REACTIVATED';
|
|
9
|
+
ALTER TYPE "public"."ActivityEventType" ADD VALUE 'USER_ROLE_CHANGED';
|
|
10
|
+
ALTER TYPE "public"."ActivityEventType" ADD VALUE 'USER_PROFILE_COMPLETED';
|
|
11
|
+
ALTER TYPE "public"."ActivityEventType" ADD VALUE 'CANDIDATE_REGISTERED';
|
|
12
|
+
ALTER TYPE "public"."ActivityEventType" ADD VALUE 'CANDIDATE_CREATED';
|
|
13
|
+
ALTER TYPE "public"."ActivityEventType" ADD VALUE 'CANDIDATE_PROFILE_COMPLETED';
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "employee_number" INTEGER;
|
|
2
|
+
ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "employee_code" TEXT;
|
|
3
|
+
|
|
4
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "User_employee_code_key" ON "User"("employee_code");
|
|
5
|
+
CREATE INDEX IF NOT EXISTS "User_organisation_id_employee_number_idx" ON "User"("organisation_id", "employee_number");
|
|
6
|
+
|
|
7
|
+
DROP TABLE IF EXISTS "analytics"."interviewer_analytics";
|
|
8
|
+
|
|
9
|
+
CREATE TABLE "analytics"."interviewer_organisation_analytics" (
|
|
10
|
+
"id" TEXT NOT NULL,
|
|
11
|
+
"organisation_id" TEXT NOT NULL,
|
|
12
|
+
"user_id" TEXT NOT NULL,
|
|
13
|
+
"name" TEXT,
|
|
14
|
+
"employee_id" TEXT,
|
|
15
|
+
"interview_count" INTEGER NOT NULL DEFAULT 0,
|
|
16
|
+
"accepted_count" INTEGER NOT NULL DEFAULT 0,
|
|
17
|
+
"rejected_count" INTEGER NOT NULL DEFAULT 0,
|
|
18
|
+
"unsure_count" INTEGER NOT NULL DEFAULT 0,
|
|
19
|
+
"ai_disagreement_count" INTEGER NOT NULL DEFAULT 0,
|
|
20
|
+
"ai_disagreement_percent" DOUBLE PRECISION,
|
|
21
|
+
"avg_difficulty" DOUBLE PRECISION,
|
|
22
|
+
"avg_supportiveness" DOUBLE PRECISION,
|
|
23
|
+
"avg_topics_coverage_percent" DOUBLE PRECISION,
|
|
24
|
+
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
25
|
+
|
|
26
|
+
CONSTRAINT "interviewer_organisation_analytics_pkey" PRIMARY KEY ("id")
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
CREATE UNIQUE INDEX "interviewer_organisation_analytics_organisation_id_user_id_key"
|
|
30
|
+
ON "analytics"."interviewer_organisation_analytics"("organisation_id", "user_id");
|
|
31
|
+
CREATE INDEX "interviewer_organisation_analytics_organisation_id_idx"
|
|
32
|
+
ON "analytics"."interviewer_organisation_analytics"("organisation_id");
|
|
33
|
+
CREATE INDEX "interviewer_organisation_analytics_organisation_id_user_id_idx"
|
|
34
|
+
ON "analytics"."interviewer_organisation_analytics"("organisation_id", "user_id");
|
|
35
|
+
|
|
36
|
+
CREATE TABLE "analytics"."interviewer_job_analytics" (
|
|
37
|
+
"id" TEXT NOT NULL,
|
|
38
|
+
"organisation_id" TEXT NOT NULL,
|
|
39
|
+
"user_id" TEXT NOT NULL,
|
|
40
|
+
"job_description_id" TEXT NOT NULL,
|
|
41
|
+
"hiring_manager_id" TEXT,
|
|
42
|
+
"department" TEXT,
|
|
43
|
+
"location" TEXT,
|
|
44
|
+
"role_title" TEXT,
|
|
45
|
+
"job_code" TEXT,
|
|
46
|
+
"opened_at" TIMESTAMP(3),
|
|
47
|
+
"name" TEXT,
|
|
48
|
+
"employee_id" TEXT,
|
|
49
|
+
"interview_count" INTEGER NOT NULL DEFAULT 0,
|
|
50
|
+
"accepted_count" INTEGER NOT NULL DEFAULT 0,
|
|
51
|
+
"rejected_count" INTEGER NOT NULL DEFAULT 0,
|
|
52
|
+
"unsure_count" INTEGER NOT NULL DEFAULT 0,
|
|
53
|
+
"ai_disagreement_count" INTEGER NOT NULL DEFAULT 0,
|
|
54
|
+
"ai_disagreement_percent" DOUBLE PRECISION,
|
|
55
|
+
"avg_difficulty" DOUBLE PRECISION,
|
|
56
|
+
"avg_supportiveness" DOUBLE PRECISION,
|
|
57
|
+
"avg_topics_coverage_percent" DOUBLE PRECISION,
|
|
58
|
+
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
59
|
+
|
|
60
|
+
CONSTRAINT "interviewer_job_analytics_pkey" PRIMARY KEY ("id")
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
CREATE UNIQUE INDEX "interviewer_job_analytics_organisation_id_user_id_job_descri_key"
|
|
64
|
+
ON "analytics"."interviewer_job_analytics"("organisation_id", "user_id", "job_description_id");
|
|
65
|
+
CREATE INDEX "interviewer_job_analytics_organisation_id_job_description_id_idx"
|
|
66
|
+
ON "analytics"."interviewer_job_analytics"("organisation_id", "job_description_id");
|
|
67
|
+
CREATE INDEX "interviewer_job_analytics_organisation_id_hiring_manager_id_idx"
|
|
68
|
+
ON "analytics"."interviewer_job_analytics"("organisation_id", "hiring_manager_id");
|
|
69
|
+
CREATE INDEX "interviewer_job_analytics_organisation_id_department_idx"
|
|
70
|
+
ON "analytics"."interviewer_job_analytics"("organisation_id", "department");
|
|
71
|
+
CREATE INDEX "interviewer_job_analytics_organisation_id_location_idx"
|
|
72
|
+
ON "analytics"."interviewer_job_analytics"("organisation_id", "location");
|
|
73
|
+
CREATE INDEX "interviewer_job_analytics_organisation_id_user_id_idx"
|
|
74
|
+
ON "analytics"."interviewer_job_analytics"("organisation_id", "user_id");
|
|
75
|
+
|
|
76
|
+
CREATE INDEX IF NOT EXISTS "job_round_analytics_organisation_id_idx"
|
|
77
|
+
ON "analytics"."job_round_analytics"("organisation_id");
|
|
78
|
+
CREATE INDEX IF NOT EXISTS "job_round_analytics_organisation_id_hiring_manager_id_idx"
|
|
79
|
+
ON "analytics"."job_round_analytics"("organisation_id", "hiring_manager_id");
|
|
80
|
+
CREATE INDEX IF NOT EXISTS "job_round_analytics_job_description_id_idx"
|
|
81
|
+
ON "analytics"."job_round_analytics"("job_description_id");
|
|
82
|
+
|
|
83
|
+
CREATE INDEX IF NOT EXISTS "pipeline_distribution_analytics_job_description_id_idx"
|
|
84
|
+
ON "analytics"."pipeline_distribution_analytics"("job_description_id");
|
|
85
|
+
|
|
86
|
+
CREATE INDEX IF NOT EXISTS "open_job_analytics_job_description_id_idx"
|
|
87
|
+
ON "analytics"."open_job_analytics"("job_description_id");
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
-- CreateEnum
|
|
2
|
+
CREATE TYPE "public"."SourceCategory" AS ENUM ('JOB_BOARD', 'REFERRAL', 'AGENCY', 'SOCIAL_MEDIA', 'CAREERS_PAGE', 'EVENT', 'OTHER');
|
|
3
|
+
|
|
4
|
+
-- CreateTable
|
|
5
|
+
CREATE TABLE "public"."Source" (
|
|
6
|
+
"id" TEXT NOT NULL,
|
|
7
|
+
"organisation_id" TEXT NOT NULL,
|
|
8
|
+
"name" TEXT NOT NULL,
|
|
9
|
+
"category" "public"."SourceCategory",
|
|
10
|
+
"parent_source_id" TEXT,
|
|
11
|
+
"colour" TEXT,
|
|
12
|
+
"description" TEXT,
|
|
13
|
+
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
|
14
|
+
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
15
|
+
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
16
|
+
|
|
17
|
+
CONSTRAINT "Source_pkey" PRIMARY KEY ("id")
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
-- CreateIndex
|
|
21
|
+
CREATE UNIQUE INDEX "Source_organisation_id_name_key" ON "public"."Source"("organisation_id", "name");
|
|
22
|
+
CREATE INDEX "Source_organisation_id_idx" ON "public"."Source"("organisation_id");
|
|
23
|
+
CREATE INDEX "Source_organisation_id_category_idx" ON "public"."Source"("organisation_id", "category");
|
|
24
|
+
CREATE INDEX "Source_organisation_id_is_active_idx" ON "public"."Source"("organisation_id", "is_active");
|
|
25
|
+
CREATE INDEX "Source_parent_source_id_idx" ON "public"."Source"("parent_source_id");
|
|
26
|
+
|
|
27
|
+
-- AddForeignKey
|
|
28
|
+
ALTER TABLE "public"."Source" ADD CONSTRAINT "Source_organisation_id_fkey" FOREIGN KEY ("organisation_id") REFERENCES "public"."Organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
29
|
+
ALTER TABLE "public"."Source" ADD CONSTRAINT "Source_parent_source_id_fkey" FOREIGN KEY ("parent_source_id") REFERENCES "public"."Source"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
30
|
+
|
|
31
|
+
-- AlterTable
|
|
32
|
+
ALTER TABLE "public"."Application" ADD COLUMN "source_id" TEXT;
|
|
33
|
+
|
|
34
|
+
-- CreateIndex
|
|
35
|
+
CREATE INDEX "Application_source_id_idx" ON "public"."Application"("source_id");
|
|
36
|
+
|
|
37
|
+
-- AddForeignKey
|
|
38
|
+
ALTER TABLE "public"."Application" ADD CONSTRAINT "Application_source_id_fkey" FOREIGN KEY ("source_id") REFERENCES "public"."Source"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|