refactor: Consolidate training and certification models into a unified schema

- Added TrainingInvitation and TrainingAttendance models to manage invitations and attendance tracking for training sessions.
- Enhanced the Training model with additional fields for scheduling, location management, and group management.
- Introduced enums for TrainingLocationType and AttendanceStatus to standardize location types and attendance tracking.
- Removed outdated schemas related to attachments, financial records, and other domains to streamline the schema structure.
- Updated documentation to reflect changes and improve clarity on training and certification management.
This commit is contained in:
Abdul Kholik Sobary 2025-07-08 12:55:56 +07:00
parent aaf60a0a2a
commit 90911b8fca
18 changed files with 170 additions and 2750 deletions

View File

@ -367,8 +367,10 @@ model Farmer {
bankAccounts FarmerBankAccount[]
vehicles FarmerVehicle[]
farms Farm[]
trainings FarmerTraining[]
certifications FarmerCertification[]
trainings FarmerTraining[]
certifications FarmerCertification[]
trainingInvitations TrainingInvitation[]
trainingAttendances TrainingAttendance[]
procurements Procurement[]
harvests Harvest[]
attachments FarmerAttachment[]
@ -2315,14 +2317,33 @@ model WeatherForecast {
}
// Training & Certification Domain
// Contains training programs, certifications, and farmer development
// Contains training programs, certifications, farmer development, and group training management
// Features: batch training, invitation system, attendance tracking, location management, documentation
// Training-specific Enums
enum TrainingStatus {
ENROLLED
IN_PROGRESS
COMPLETED
DROPPED
ENROLLED // Farmer enrolled in training program
IN_PROGRESS // Currently participating in training
COMPLETED // Successfully completed training
DROPPED // Dropped out of training program
}
enum TrainingLocationType {
VILLAGE_HALL // Training held at village hall/balai desa
FARM_FIELD // Field training at farm/kebun
FARMER_HOME // Training at farmer's home
COOPERATIVE // Training at cooperative office
GOVERNMENT_OFFICE // Training at government facility
ONLINE // Virtual/online training
OTHER // Other location type
}
enum AttendanceStatus {
INVITED // Farmer invited to training
CONFIRMED // Farmer confirmed attendance
ATTENDED // Farmer attended the training
ABSENT // Farmer was absent
CANCELLED // Invitation/attendance cancelled
}
enum CertificationStatus {
@ -2335,27 +2356,48 @@ enum CertificationStatus {
// Training Models
model Training {
id String @id @default(cuid())
title String
description String?
content String?
category String
duration Int? // in minutes
level String? // beginner, intermediate, advanced
prerequisites String?
maxParticipants Int? // Maximum number of participants
createdBy String? // Admin who created the training
title String // Training title/name
description String? // Training description
content String? // Training content/materials
category String // Training category (e.g., "Pest Management", "Organic Farming")
duration Int? // Training duration in minutes
level String? // Training level: beginner, intermediate, advanced
prerequisites String? // Prerequisites for joining training
maxParticipants Int? // Maximum number of participants allowed
createdBy String? // Admin ID who created the training
// Training schedule and location management
scheduledDate DateTime? // Date when training is scheduled
startTime DateTime? // Training start time
endTime DateTime? // Training end time
locationType TrainingLocationType? // Type of training location
location String? // Specific location name/description
locationAddress String? // Full address of training location
locationGPS Json? // GeoJSON point for precise location coordinates
// Group/batch management for farmer groups
batchName String? // Name identifier for the training batch
targetGroup String? // Description of target audience/group
kelompokTaniIds String[] // Array of farmer group IDs (SH/kelompok tani) participating
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmerTrainings FarmerTraining[]
farmerTrainings FarmerTraining[] // Individual farmer training records
trainingInvitations TrainingInvitation[] // Training invitations sent to farmers
trainingAttendances TrainingAttendance[] // Attendance records for the training
trainingDocuments TrainingDocument[] // Photos, videos, and documents related to training
@@index([category])
@@index([level])
@@index([isActive])
@@index([duration])
@@index([createdBy])
@@index([scheduledDate])
@@index([locationType])
@@index([batchName])
@@map("trainings")
}
@ -2390,6 +2432,117 @@ model FarmerTraining {
@@map("farmer_trainings")
}
model TrainingInvitation {
id String @id @default(cuid())
trainingId String // Reference to Training
farmerId String // Reference to Farmer being invited
invitedBy String? // Admin ID who created the invitation
invitedAt DateTime @default(now()) // When invitation was sent
message String? // Custom invitation message for the farmer
// Response tracking - farmer's response to invitation
responseStatus AttendanceStatus @default(INVITED) // Current status of farmer's response
respondedAt DateTime? // When farmer responded to invitation
responseNote String? // Farmer's note/comment when responding
// Reminder system for follow-up
reminderSent Boolean @default(false) // Whether reminder has been sent
reminderSentAt DateTime? // When last reminder was sent
reminderCount Int @default(0) // Number of reminders sent
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
training Training @relation(fields: [trainingId], references: [id], onDelete: Cascade)
farmer Farmer @relation(fields: [farmerId], references: [id], onDelete: Cascade)
@@unique([trainingId, farmerId]) // One invitation per farmer per training
@@index([trainingId])
@@index([farmerId])
@@index([responseStatus])
@@index([invitedAt])
@@index([reminderSent])
@@map("training_invitations")
}
model TrainingAttendance {
id String @id @default(cuid())
trainingId String // Reference to Training
farmerId String // Reference to Farmer
attendanceStatus AttendanceStatus @default(INVITED) // Attendance status
// Check-in/out tracking system for training sessions
checkInTime DateTime? // When farmer checked in to training
checkOutTime DateTime? // When farmer checked out of training
checkInBy String? // Admin ID who recorded the check-in
checkOutBy String? // Admin ID who recorded the check-out
// Participation evaluation and feedback
participationScore Float? // 1-5 rating of farmer's participation quality
completedTasks Boolean @default(false) // Whether farmer completed training tasks
feedback String? // Farmer's feedback about the training experience
notes String? // Additional notes about attendance
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
training Training @relation(fields: [trainingId], references: [id], onDelete: Cascade)
farmer Farmer @relation(fields: [farmerId], references: [id], onDelete: Cascade)
@@unique([trainingId, farmerId]) // One attendance record per farmer per training
@@index([trainingId])
@@index([farmerId])
@@index([attendanceStatus])
@@index([checkInTime])
@@index([participationScore])
@@map("training_attendances")
}
model TrainingDocument {
id String @id @default(cuid())
trainingId String // Reference to Training
title String // Document title/name
description String? // Document description
fileUrl String // URL/path to the file
fileName String // Original filename
fileSize Int? // File size in bytes
mimeType String? // File MIME type (image/jpeg, video/mp4, etc.)
// Document type and categorization
documentType String? // Type: photo, video, certificate, handout, presentation, etc.
category String? // Category: documentation, material, certificate, evaluation
// Metadata and tracking information
uploadedBy String? // User ID who uploaded the document
takenBy String? // Person who took the photo/video (photographer/videographer)
takenAt DateTime? // When the photo/video was taken during training
gpsLocation Json? // GeoJSON point coordinates where photo/video was taken
// Verification and quality control
isVerified Boolean @default(false) // Whether document has been verified by admin
verifiedBy String? // Admin ID who verified the document
verifiedAt DateTime? // When document was verified
// Access control
isPublic Boolean @default(false) // Whether document is visible to training participants
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
training Training @relation(fields: [trainingId], references: [id], onDelete: Cascade)
@@index([trainingId])
@@index([documentType])
@@index([category])
@@index([isVerified])
@@index([isPublic])
@@index([uploadedBy])
@@index([takenAt])
@@map("training_documents")
}
model Certification {
id String @id @default(cuid())
name String

View File

@ -1,214 +0,0 @@
# Split Schema Structure
This directory contains the split version of the agricultural platform's Prisma schema, organized by domain for better maintainability and team collaboration.
## Directory Structure
```
schemas/
├── README.md # This file
├── base.prisma # Generator, datasource, and shared enums
├── user.prisma # User management (User, Farmer, Buyer, Administrator)
├── reference.prisma # Reference data (Country, Currency, Religion, EducationLevel)
├── farm.prisma # Farm management (Farm, Plot, SoilTest, FarmAttachment)
├── product.prisma # Product system (Product, ProductVariant, modifiers)
├── procurement.prisma # Trading (Procurement, Contract, Harvest)
├── season.prisma # Seasonal planning (Season, PlotSeason, analytics)
├── operations.prisma # Operations (Input, Labor, Equipment, Assets)
├── financial.prisma # Financial records and transactions
├── communication.prisma # Notifications and messaging
├── weather.prisma # Weather data and forecasts
├── training.prisma # Training and certification
├── market.prisma # Market intelligence and pricing
├── knowledge.prisma # Articles and knowledge base
├── attachments.prisma # File and attachment management
└── pest-disease.prisma # Pest, disease, and review management
```
## Benefits of Split Schema
### 1. **Better Organization**
- Logical grouping by business domain
- Easier to find specific models
- Reduced cognitive load when working on specific features
- Clear separation of concerns
### 2. **Team Collaboration**
- Multiple developers can work on different schema files simultaneously
- Reduced merge conflicts
- Clear ownership boundaries
- Focused code reviews
### 3. **Maintainability**
- Easier to understand and modify specific domains
- Better debugging and troubleshooting
- Cleaner dependency management
- Improved documentation per domain
### 4. **Performance Benefits**
- Faster schema compilation for individual domains
- Easier to identify bottlenecks
- Better indexing strategy per domain
- Optimized queries per business area
## How to Use Split Schema
### Option 1: Replace Original Schema
1. **Backup original schema.prisma**:
```bash
mv schema.prisma schema.prisma.backup
```
2. **Use split schema directory**:
```bash
# Prisma will automatically read all .prisma files in the directory
npx prisma generate
npx prisma migrate dev
```
### Option 2: Parallel Development
Keep both structures and choose based on your needs:
- Use original `schema.prisma` for production
- Use `schemas/` directory for development and feature work
- Gradually migrate teams to split structure
## Domain Responsibilities
### **Core Domains**
- **user.prisma**: Authentication, user profiles, role management
- **reference.prisma**: Master data (countries, currencies, education levels)
- **farm.prisma**: Farm infrastructure, plots, soil management
### **Business Domains**
- **product.prisma**: Product catalog, variants, quality modifiers
- **procurement.prisma**: Trading, contracts, harvest management
- **season.prisma**: Agricultural cycles, seasonal planning
- **operations.prisma**: Daily operations, labor, equipment
### **Support Domains**
- **financial.prisma**: Financial tracking, transactions
- **communication.prisma**: Notifications, messaging
- **weather.prisma**: Climate data, forecasts
- **training.prisma**: Education, certifications
### **Analytics Domains**
- **market.prisma**: Market intelligence, pricing
- **knowledge.prisma**: Content management
- **attachments.prisma**: File management
- **pest-disease.prisma**: Agricultural health tracking
## File Size Comparison
| Structure | File Count | Lines per File | Total Lines |
|-----------|------------|----------------|-------------|
| Original | 1 file | ~1,800 lines | 1,800 |
| Split | 15 files | ~120-300 lines| 1,800 |
## Cross-Domain Relationships
The split maintains all relationships between domains:
```prisma
// farmer.prisma references farm.prisma
model Farmer {
farms Farm[] // → farm.prisma
}
// farm.prisma references farmer.prisma
model Farm {
farmer Farmer @relation(...) // → user.prisma
}
```
## Migration Strategy
### Phase 1: Parallel Structure
- Keep original schema.prisma
- Create split structure alongside
- Use for new feature development
### Phase 2: Team Adoption
- Train teams on domain boundaries
- Establish ownership per domain
- Use split structure for reviews
### Phase 3: Full Migration
- Move production to split structure
- Archive original schema.prisma
- Update CI/CD pipelines
## Best Practices
### 1. **Domain Boundaries**
- Keep related models together
- Minimize cross-domain dependencies
- Use clear naming conventions
### 2. **Shared Elements**
- Keep shared enums in base.prisma
- Document cross-domain relationships
- Maintain consistent data types
### 3. **Team Workflow**
- Assign domain ownership
- Review changes by domain
- Coordinate cross-domain changes
### 4. **Development**
- Test schema compilation regularly
- Validate relationships across files
- Monitor performance impact
## Troubleshooting
### Common Issues
1. **Duplicate Models/Enums**:
- Ensure original schema.prisma is not in the same directory
- Check for naming conflicts across files
2. **Missing Relations**:
- Verify cross-file relationships are properly defined
- Check import paths and model references
3. **Compilation Errors**:
- Run `npx prisma validate` to check schema integrity
- Verify all referenced models exist across files
### Validation Commands
```bash
# Validate entire schema
npx prisma validate
# Generate client to test compilation
npx prisma generate
# Check database sync
npx prisma migrate dev --create-only
```
## Future Enhancements
### Potential Improvements
1. **Microservice Extraction**: Easier to identify service boundaries
2. **Database Sharding**: Clear data partitioning strategies
3. **Team Scaling**: Better support for large development teams
4. **Domain-Driven Design**: Enhanced DDD implementation
### Monitoring
- Track file modification patterns
- Monitor domain interaction frequency
- Identify optimization opportunities
## Support
For questions about the split schema structure:
1. Check this README for common issues
2. Review domain documentation in each file
3. Validate schema compilation with Prisma CLI
4. Contact the database team for complex migrations
---
**Note**: This split structure maintains full compatibility with the original schema.prisma. All relationships, constraints, and data types remain identical.

View File

@ -1,45 +0,0 @@
// Attachment Management Domain
// Contains all attachment and file management models
// Attachment-specific Enums
enum AttachmentType {
PROFILE_PHOTO
ID_CARD_FRONT
ID_CARD_BACK
FARMING_LICENSE
LAND_CERTIFICATE
BANK_STATEMENT
CONTRACT
CERTIFICATE
OTHER_DOCUMENT
}
// Attachment Models
model FarmerAttachment {
id String @id @default(cuid())
farmerId String
type AttachmentType
title String
description String?
fileUrl String
fileName String
fileSize Int? // in bytes
mimeType String?
uploadedBy String? // user ID who uploaded
isVerified Boolean @default(false)
verifiedBy String? // admin ID who verified
verifiedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer @relation(fields: [farmerId], references: [id], onDelete: Cascade)
@@index([farmerId])
@@index([type])
@@index([isVerified])
@@index([uploadedBy])
@@index([createdAt])
@@index([mimeType])
@@map("farmer_attachments")
}

View File

@ -1,205 +0,0 @@
// This is the base Prisma schema file
// Contains generator, datasource, and shared enums
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// Shared Enums - Used across multiple domains
// Core User Management Enums
enum UserRole {
FARMER
BUYER
ADMINISTRATOR
}
enum Gender {
MALE
FEMALE
OTHER
}
enum MaritalStatus {
SINGLE
MARRIED
DIVORCED
WIDOWED
SEPARATED
}
enum IdentityType {
KTP // Indonesian ID Card
PASSPORT
DRIVING_LICENSE
OTHER
}
// Quality and Status Enums
enum QualityGrade {
A
B
C
}
enum PaymentMethod {
CASH
BANK_TRANSFER
MOBILE_MONEY
CHECK
DIGITAL_WALLET
CREDIT
BARTER
INSTALLMENT
}
enum PaymentStatus {
PENDING
APPROVED
PAID
OVERDUE
CANCELLED
}
enum ScheduleStatus {
PENDING
SCHEDULED
IN_PROGRESS
COMPLETED
CANCELLED
OVERDUE
}
// Geographic and Environmental Enums
enum WaterSource {
RAIN_FED
IRRIGATION_CANAL
WELL
RIVER
POND
GROUNDWATER
SPRING
MIXED
}
enum IrrigationType {
FLOOD
SPRINKLER
DRIP
FURROW
MANUAL
NONE
}
enum SlopeType {
FLAT
GENTLE
MODERATE
STEEP
VERY_STEEP
}
enum ClimateType {
TROPICAL_WET
TROPICAL_DRY
SUBTROPICAL
TEMPERATE
HIGHLAND
}
enum RoadType {
PAVED
GRAVEL
DIRT
FOOTPATH
NO_ACCESS
}
// Agricultural Enums
enum WorkType {
PLANTING
WEEDING
FERTILIZING
HARVESTING
IRRIGATION
PEST_CONTROL
SOIL_PREPARATION
PRUNING
GENERAL_MAINTENANCE
EQUIPMENT_OPERATION
PROCESSING
PACKAGING
TRANSPORT
}
enum InputType {
SEED
FERTILIZER
PESTICIDE
HERBICIDE
FUNGICIDE
INSECTICIDE
EQUIPMENT_RENTAL
FUEL
IRRIGATION_WATER
MULCH
COMPOST
LIME
OTHER
}
enum SeverityLevel {
LOW
MEDIUM
HIGH
CRITICAL
CATASTROPHIC
}
// Communication and Priority Enums
enum Priority {
LOW
NORMAL
HIGH
URGENT
CRITICAL
}
enum MessageType {
PERSONAL
BROADCAST
NOTIFICATION
ALERT
SYSTEM
ANNOUNCEMENT
}
enum MessageStatus {
SENT
DELIVERED
READ
FAILED
PENDING
}
// Market Intelligence Enums
enum PriceTrend {
RISING
FALLING
STABLE
VOLATILE
}
enum DemandLevel {
VERY_LOW
LOW
MODERATE
HIGH
VERY_HIGH
EXCESSIVE
}

View File

@ -1,160 +0,0 @@
// Communication & Notifications Domain
// Contains Notification, Message, and communication models
// Communication-specific Enums
enum NotificationType {
SYSTEM
ANNOUNCEMENT
ALERT
REMINDER
PROMOTION
UPDATE
WARNING
INFO
SUCCESS
ERROR
}
enum NotificationCategory {
GENERAL
PROCUREMENT
PAYMENT
QUALITY
WEATHER
PRICE_ALERT
TRAINING
CERTIFICATION
MAINTENANCE
HARVEST
PLANTING
MARKET
CONTRACT
COMPLIANCE
}
enum NotificationStatus {
PENDING
SCHEDULED
SENT
DELIVERED
FAILED
CANCELLED
EXPIRED
}
// Communication Models
model Notification {
id String @id @default(cuid())
title String
message String
type NotificationType
category NotificationCategory @default(GENERAL)
priority Priority @default(NORMAL)
// Recipients
recipientId String? // specific user ID
recipientType UserRole? // or broadcast to user type
recipientIds String[] // multiple specific users
// Targeting
farmerIds String[] // specific farmers
buyerIds String[] // specific buyers
region String? // geographic targeting
productIds String[] // product-specific notifications
// Content and media
content String? // detailed content/body
imageUrl String?
actionUrl String? // deep link or action URL
actionLabel String? // button text
// Scheduling
scheduledAt DateTime? // for scheduled notifications
expiresAt DateTime? // expiration date
// Status tracking
status NotificationStatus @default(PENDING)
sentAt DateTime?
deliveredCount Int @default(0)
readCount Int @default(0)
clickCount Int @default(0)
// Metadata
source String? // system, admin, automated, etc.
sourceId String? // reference to source entity
tags String[] // for categorization
metadata Json? // additional data
// Tracking
isRead Boolean @default(false)
readAt DateTime?
isClicked Boolean @default(false)
clickedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
recipients NotificationRecipient[]
@@index([type])
@@index([category])
@@index([priority])
@@index([status])
@@index([scheduledAt])
@@index([recipientType])
@@index([region])
@@index([createdAt])
@@map("notifications")
}
model NotificationRecipient {
id String @id @default(cuid())
notificationId String
userId String
isRead Boolean @default(false)
readAt DateTime?
isClicked Boolean @default(false)
clickedAt DateTime?
isDelivered Boolean @default(false)
deliveredAt DateTime?
createdAt DateTime @default(now())
// Relations
notification Notification @relation(fields: [notificationId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([notificationId, userId])
@@index([notificationId])
@@index([userId])
@@index([isRead])
@@index([isDelivered])
@@index([createdAt])
@@map("notification_recipients")
}
model Message {
id String @id @default(cuid())
senderId String
receiverId String?
groupId String?
subject String?
content String
messageType MessageType
priority Priority @default(NORMAL)
isRead Boolean @default(false)
readAt DateTime?
attachments String[] // file URLs
deliveryStatus MessageStatus @default(SENT)
sentAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([senderId])
@@index([receiverId])
@@index([messageType])
@@index([isRead])
@@index([sentAt])
@@index([deliveryStatus])
@@map("messages")
}

View File

@ -1,249 +0,0 @@
// Farm Management Domain
// Contains Farm, Plot, and related farming infrastructure models
// Farm-specific Enums
enum FarmOwnership {
PRIVATE_OWNED
LEASED
SHARECROPPED
COOPERATIVE
GOVERNMENT
COMMUNAL
FAMILY_INHERITED
}
enum FarmingSystem {
MONOCULTURE
POLYCULTURE
MIXED_FARMING
ORGANIC
CONVENTIONAL
INTEGRATED
PERMACULTURE
AGROFORESTRY
}
enum FarmAttachmentType {
MAIN_PHOTO
AERIAL_PHOTO
SOIL_PHOTO
CROP_PHOTO
INFRASTRUCTURE_PHOTO
LAND_CERTIFICATE
SURVEY_MAP
WATER_SOURCE_PHOTO
ENTRANCE_PHOTO
BOUNDARY_PHOTO
EQUIPMENT_PHOTO
STORAGE_PHOTO
OTHER_DOCUMENT
}
// Farm Models
model Farm {
id String @id @default(cuid())
farmerId String
farmCode String @unique
name String
address String?
village String?
district String?
province String?
postalCode String?
area Decimal? // in hectares
// Location data
latitude Float?
longitude Float?
boundaries Json? // GeoJSON polygon for farm boundaries
elevation Float? // meters above sea level
// Farm characteristics
soilType String?
soilPH Float?
waterSource WaterSource?
irrigationType IrrigationType?
slope SlopeType?
climate ClimateType?
// Infrastructure
hasElectricity Boolean? @default(false)
hasWaterAccess Boolean? @default(false)
hasStorageFacility Boolean? @default(false)
hasProcessingUnit Boolean? @default(false)
accessRoadType RoadType?
// Photos and documentation
mainPhotoUrl String?
aerialPhotoUrl String?
soilPhotoUrl String?
// Ownership and legal
ownershipType FarmOwnership?
landCertificateNumber String?
landCertificateUrl String?
// Agricultural details
establishedYear Int?
totalInvestment Decimal?
annualProduction Decimal? // estimated kg per year
mainCrops String[] // primary crops grown
farmingSystem FarmingSystem?
organicCertified Boolean? @default(false)
description String?
notes String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer @relation(fields: [farmerId], references: [id], onDelete: Cascade)
plots Plot[]
harvests Harvest[]
attachments FarmAttachment[]
weatherData WeatherData[]
weatherForecasts WeatherForecast[]
inputs FarmInput[]
inputSchedules InputSchedule[]
laborRecords LaborRecord[]
laborSchedules LaborSchedule[]
equipmentUsage EquipmentUsage[]
pestDiseaseRecords PestDiseaseRecord[]
financialRecords FinancialRecord[]
soilTests SoilTest[]
@@index([farmCode])
@@index([farmerId])
@@index([latitude, longitude])
@@index([isActive])
@@index([establishedYear])
@@map("farms")
}
model Plot {
id String @id @default(cuid())
farmId String
name String
area Decimal? // in hectares
productId String?
variantId String?
plantedDate DateTime?
boundaries Json? // GeoJSON polygon for plot boundaries
description String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farm Farm @relation(fields: [farmId], references: [id], onDelete: Cascade)
product Product? @relation(fields: [productId], references: [id])
variant ProductVariant? @relation(fields: [variantId], references: [id])
harvests Harvest[]
weatherData WeatherData[]
weatherForecasts WeatherForecast[]
inputs FarmInput[]
inputSchedules InputSchedule[]
laborRecords LaborRecord[]
laborSchedules LaborSchedule[]
equipmentUsage EquipmentUsage[]
pestDiseaseRecords PestDiseaseRecord[]
financialRecords FinancialRecord[]
soilTests SoilTest[]
seasons PlotSeason[]
@@index([farmId])
@@index([productId, variantId])
@@index([plantedDate])
@@index([isActive])
@@map("plots")
}
model FarmAttachment {
id String @id @default(cuid())
farmId String
type FarmAttachmentType
title String
description String?
fileUrl String
fileName String
fileSize Int? // in bytes
mimeType String?
uploadedBy String? // user ID who uploaded
takenDate DateTime? // when photo was taken
gpsLocation Json? // GeoJSON point where photo was taken
isVerified Boolean @default(false)
verifiedBy String? // admin ID who verified
verifiedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farm Farm @relation(fields: [farmId], references: [id], onDelete: Cascade)
@@index([farmId])
@@index([type])
@@index([isVerified])
@@index([createdAt])
@@map("farm_attachments")
}
model SoilTest {
id String @id @default(cuid())
farmId String
plotId String?
testCode String @unique
testDate DateTime
sampleDepth Decimal? // cm
sampleLocation String?
gpsCoordinates Json? // GeoJSON point
// Basic soil properties
pH Decimal?
organicMatter Decimal? // percentage
soilTexture String? // clay, sand, loam, etc.
bulkDensity Decimal? // g/cm³
porosity Decimal? // percentage
// Nutrients (ppm)
nitrogen Decimal?
phosphorus Decimal?
potassium Decimal?
calcium Decimal?
magnesium Decimal?
sulfur Decimal?
// Micronutrients (ppm)
zinc Decimal?
iron Decimal?
manganese Decimal?
copper Decimal?
boron Decimal?
// Other properties
conductivity Decimal? // dS/m
cationExchangeCapacity Decimal? // cmol/kg
baseStaturation Decimal? // percentage
carbonNitrogenRatio Decimal?
// Analysis details
recommendations String?
testLaboratory String?
technicianName String?
testCost Decimal?
reportUrl String?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farm Farm @relation(fields: [farmId], references: [id])
plot Plot? @relation(fields: [plotId], references: [id])
@@index([testCode])
@@index([farmId, plotId])
@@index([testDate])
@@index([testLaboratory])
@@map("soil_tests")
}

View File

@ -1,68 +0,0 @@
// Financial Management Domain
// Contains financial records, transactions, and currency models
// Financial-specific Enums
enum TransactionType {
INCOME
EXPENSE
INVESTMENT
LOAN
LOAN_PAYMENT
INSURANCE_PAYMENT
TAX_PAYMENT
GRANT
SUBSIDY
REFUND
}
enum TransactionStatus {
PENDING
APPROVED
COMPLETED
REJECTED
CANCELLED
FAILED
}
// Financial Models
model FinancialRecord {
id String @id @default(cuid())
farmerId String
farmId String?
plotId String?
type TransactionType
category String
subcategory String?
amount Decimal
currencyId String @default("idr")
currency Currency @relation(fields: [currencyId], references: [id])
description String
transactionDate DateTime
paymentMethod PaymentMethod?
receiptNumber String?
invoiceNumber String?
referenceNumber String?
taxAmount Decimal? @default(0)
bankAccount String?
payee String? // who received payment
approvedBy String? // admin who approved
status TransactionStatus @default(PENDING)
notes String?
attachmentUrls String[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer @relation(fields: [farmerId], references: [id])
farm Farm? @relation(fields: [farmId], references: [id])
plot Plot? @relation(fields: [plotId], references: [id])
@@index([farmerId, farmId])
@@index([type])
@@index([transactionDate])
@@index([status])
@@index([category])
@@index([paymentMethod])
@@index([amount])
@@map("financial_records")
}

View File

@ -1,33 +0,0 @@
// Knowledge Management Domain
// Contains articles, content management, and knowledge base
// Knowledge-specific Enums
enum ArticleStatus {
DRAFT
PUBLISHED
ARCHIVED
}
// Knowledge Models
model Article {
id String @id @default(cuid())
title String
content String
excerpt String?
category String
tags String[]
author String?
status ArticleStatus @default(DRAFT)
views BigInt @default(0)
publishedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([category])
@@index([status])
@@index([publishedAt])
@@index([author])
@@index([views])
@@index([title])
@@map("articles")
}

View File

@ -1,63 +0,0 @@
// Market Intelligence Domain
// Contains market prices, demand analysis, and business intelligence
model MarketPrice {
id String @id @default(cuid())
productId String
variantId String?
market String
region String
price Decimal
unit String @default("kg")
qualityGrade QualityGrade
priceDate DateTime
source String? // where price data came from
volume Decimal? // trading volume
trend PriceTrend?
verified Boolean @default(false)
verifiedBy String?
notes String?
createdAt DateTime @default(now())
// Relations
product Product @relation(fields: [productId], references: [id])
variant ProductVariant? @relation(fields: [variantId], references: [id])
@@index([productId, variantId])
@@index([market, region])
@@index([priceDate])
@@index([qualityGrade])
@@index([trend])
@@index([verified])
@@index([price])
@@map("market_prices")
}
model MarketDemand {
id String @id @default(cuid())
productId String
variantId String?
region String
demandLevel DemandLevel
estimatedVolume Decimal?
priceRange String?
season String?
factors String[] // factors affecting demand
forecastDate DateTime
forecastBy String?
accuracy Decimal? // percentage
notes String?
createdAt DateTime @default(now())
// Relations
product Product @relation(fields: [productId], references: [id])
variant ProductVariant? @relation(fields: [variantId], references: [id])
@@index([productId, variantId])
@@index([region])
@@index([demandLevel])
@@index([forecastDate])
@@index([season])
@@index([accuracy])
@@map("market_demand")
}

View File

@ -1,439 +0,0 @@
// Operations Management Domain
// Contains Input management, Labor, Equipment, and daily operations
// Operations-specific Enums
enum SupplierType {
SEED_SUPPLIER
FERTILIZER_SUPPLIER
PESTICIDE_SUPPLIER
EQUIPMENT_SUPPLIER
GENERAL_SUPPLIER
COOPERATIVE
GOVERNMENT_AGENCY
}
enum WorkerRole {
PERMANENT
SEASONAL
DAILY
CONTRACTOR
SUPERVISOR
FOREMAN
SPECIALIST
}
enum SkillLevel {
BEGINNER
INTERMEDIATE
ADVANCED
EXPERT
}
enum ContractType {
PERMANENT
TEMPORARY
SEASONAL
PROJECT_BASED
DAILY
}
enum EquipmentType {
TRACTOR
HARVESTER
PLANTER
CULTIVATOR
IRRIGATION_SYSTEM
SPRAYER
THRESHER
MOWER
TOOLS
VEHICLE
PROCESSING_EQUIPMENT
STORAGE_EQUIPMENT
}
enum EquipmentCondition {
EXCELLENT
GOOD
FAIR
POOR
NEEDS_REPAIR
OUT_OF_ORDER
}
enum EquipmentStatus {
ACTIVE
INACTIVE
MAINTENANCE
REPAIR
RETIRED
}
enum MaintenanceType {
PREVENTIVE
CORRECTIVE
EMERGENCY
OVERHAUL
INSPECTION
CALIBRATION
}
enum AssetType {
BUILDING
LAND_IMPROVEMENT
INFRASTRUCTURE
VEHICLE
MACHINERY
FURNITURE
TECHNOLOGY
OTHER
}
enum AssetCondition {
NEW
EXCELLENT
GOOD
FAIR
POOR
DAMAGED
}
// Operations Models
model FarmInput {
id String @id @default(cuid())
farmId String
plotId String?
inputType InputType
productName String
brand String?
quantity Decimal
unit String
cost Decimal
supplier String?
supplierContact String?
batchNumber String?
expiryDate DateTime?
applicationDate DateTime?
applicationMethod String?
applicationRate String? // per hectare or per plant
activeIngredient String? // for pesticides/fertilizers
concentration Decimal? // percentage
notes String?
invoiceNumber String?
receiptUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farm Farm @relation(fields: [farmId], references: [id])
plot Plot? @relation(fields: [plotId], references: [id])
@@index([farmId, plotId])
@@index([inputType])
@@index([applicationDate])
@@index([supplier])
@@index([expiryDate])
@@map("farm_inputs")
}
model InputSchedule {
id String @id @default(cuid())
farmId String
plotId String?
inputType InputType
productName String
scheduledDate DateTime
quantity Decimal
unit String
method String?
status ScheduleStatus @default(PENDING)
appliedDate DateTime?
appliedBy String?
actualQuantity Decimal?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farm Farm @relation(fields: [farmId], references: [id])
plot Plot? @relation(fields: [plotId], references: [id])
@@index([farmId, plotId])
@@index([scheduledDate])
@@index([status])
@@index([inputType])
@@map("input_schedules")
}
model Supplier {
id String @id @default(cuid())
name String
contactPerson String?
phone String?
email String?
address String?
supplierType SupplierType
paymentTerms String?
deliveryTerms String?
qualityCertifications String[]
isActive Boolean @default(true)
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([supplierType])
@@index([isActive])
@@index([name])
@@map("suppliers")
}
model FarmWorker {
id String @id @default(cuid())
farmerId String
workerCode String @unique
name String
phone String?
email String?
address String?
identityNumber String?
role WorkerRole
skillLevel SkillLevel?
dailyWage Decimal?
monthlyWage Decimal?
paymentMethod PaymentMethod?
bankAccount String?
emergencyContact String?
emergencyPhone String?
hireDate DateTime?
contractType ContractType?
contractEnd DateTime?
isActive Boolean @default(true)
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer @relation(fields: [farmerId], references: [id])
laborRecords LaborRecord[]
laborSchedules LaborSchedule[]
@@index([workerCode])
@@index([farmerId])
@@index([role])
@@index([isActive])
@@index([contractType])
@@map("farm_workers")
}
model LaborRecord {
id String @id @default(cuid())
farmerId String
farmId String
workerId String?
plotId String?
workType WorkType
hoursWorked Decimal
wages Decimal
workDate DateTime
startTime DateTime?
endTime DateTime?
description String?
supervisor String? // supervisor name or ID
qualityRating Decimal? // 1-5 rating
weather String?
notes String?
approved Boolean @default(false)
approvedBy String?
approvedDate DateTime?
paymentStatus PaymentStatus @default(PENDING)
paidDate DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer @relation(fields: [farmerId], references: [id])
farm Farm @relation(fields: [farmId], references: [id])
worker FarmWorker? @relation(fields: [workerId], references: [id])
plot Plot? @relation(fields: [plotId], references: [id])
@@index([farmerId, farmId])
@@index([workDate])
@@index([workType])
@@index([paymentStatus])
@@index([workerId])
@@map("labor_records")
}
model LaborSchedule {
id String @id @default(cuid())
farmerId String
farmId String
workerId String?
plotId String?
workType WorkType
scheduledDate DateTime
estimatedHours Decimal
estimatedWage Decimal?
status ScheduleStatus @default(PENDING)
assignedBy String?
notes String?
actualRecord String? // reference to LaborRecord ID
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer @relation(fields: [farmerId], references: [id])
farm Farm @relation(fields: [farmId], references: [id])
worker FarmWorker? @relation(fields: [workerId], references: [id])
plot Plot? @relation(fields: [plotId], references: [id])
@@index([farmerId, farmId])
@@index([scheduledDate])
@@index([status])
@@index([workType])
@@index([workerId])
@@map("labor_schedules")
}
model Equipment {
id String @id @default(cuid())
farmerId String
equipmentCode String @unique
name String
type EquipmentType
brand String?
model String?
serialNumber String?
purchaseDate DateTime?
purchasePrice Decimal?
currentValue Decimal?
condition EquipmentCondition
status EquipmentStatus @default(ACTIVE)
location String? // where equipment is stored
fuelType String? // diesel, petrol, electric, manual
capacity String? // engine capacity, load capacity
powerRating String? // horsepower, wattage
yearManufactured Int?
warranty String?
insurancePolicy String?
insuranceExpiry DateTime?
lastMaintenance DateTime?
nextMaintenance DateTime?
maintenanceCost Decimal?
operatingHours Decimal? // total operating hours
isActive Boolean @default(true)
notes String?
photoUrl String?
manualUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer @relation(fields: [farmerId], references: [id])
maintenanceRecords MaintenanceRecord[]
usageRecords EquipmentUsage[]
@@index([equipmentCode])
@@index([farmerId])
@@index([type])
@@index([status])
@@index([nextMaintenance])
@@index([isActive])
@@map("equipment")
}
model MaintenanceRecord {
id String @id @default(cuid())
equipmentId String
maintenanceType MaintenanceType
description String
cost Decimal
serviceProvider String?
serviceDate DateTime
nextServiceDue DateTime?
partsReplaced String[]
laborHours Decimal?
invoiceNumber String?
receiptUrl String?
performedBy String?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
equipment Equipment @relation(fields: [equipmentId], references: [id])
@@index([equipmentId])
@@index([serviceDate])
@@index([maintenanceType])
@@index([nextServiceDue])
@@map("maintenance_records")
}
model EquipmentUsage {
id String @id @default(cuid())
equipmentId String
farmId String?
plotId String?
operatorName String?
usageDate DateTime
startTime DateTime?
endTime DateTime?
hoursUsed Decimal
fuelConsumed Decimal?
workType WorkType?
description String?
meterReading Decimal? // odometer, hour meter reading
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
equipment Equipment @relation(fields: [equipmentId], references: [id])
farm Farm? @relation(fields: [farmId], references: [id])
plot Plot? @relation(fields: [plotId], references: [id])
@@index([equipmentId])
@@index([usageDate])
@@index([farmId, plotId])
@@index([workType])
@@map("equipment_usage")
}
model Asset {
id String @id @default(cuid())
farmerId String
assetCode String @unique
name String
type AssetType
category String? // building, land improvement, infrastructure
description String?
purchaseDate DateTime?
purchasePrice Decimal?
currentValue Decimal?
depreciation Decimal? // annual depreciation rate
condition AssetCondition
location String?
size String? // dimensions, area
material String? // construction material
lifespan Int? // expected lifespan in years
warrantyExpiry DateTime?
insurancePolicy String?
insuranceExpiry DateTime?
photoUrls String[]
documentUrls String[]
isActive Boolean @default(true)
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer @relation(fields: [farmerId], references: [id])
@@index([assetCode])
@@index([farmerId])
@@index([type])
@@index([condition])
@@index([isActive])
@@map("assets")
}

View File

@ -1,86 +0,0 @@
// Pest & Disease Management Domain
// Contains pest, disease, and agricultural health tracking
// Pest & Disease Enums
enum PestDiseaseType {
PEST
DISEASE
WEED
NUTRIENT_DEFICIENCY
VIRUS
FUNGUS
BACTERIA
}
enum ReviewType {
PAYMENT_TIMELINESS
QUALITY_REQUIREMENTS
COMMUNICATION
OVERALL_EXPERIENCE
PRICE_FAIRNESS
}
// Pest & Disease Models
model PestDiseaseRecord {
id String @id @default(cuid())
farmId String
plotId String?
type PestDiseaseType
name String
scientificName String?
severity SeverityLevel
affectedArea Decimal? // percentage or hectares
identifiedDate DateTime
identifiedBy String? // farmer, expert, etc.
symptoms String?
treatmentApplied String?
treatmentDate DateTime?
treatmentCost Decimal?
treatmentMethod String?
preventionTaken String?
resolved Boolean @default(false)
resolvedDate DateTime?
recurrence Boolean @default(false)
photos String[] // photo URLs
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farm Farm @relation(fields: [farmId], references: [id])
plot Plot? @relation(fields: [plotId], references: [id])
@@index([farmId, plotId])
@@index([type])
@@index([severity])
@@index([identifiedDate])
@@index([resolved])
@@index([name])
@@index([recurrence])
@@map("pest_disease_records")
}
model BuyerReview {
id String @id @default(cuid())
buyerId String
farmerId String
rating Decimal // 1-5 rating
comment String?
reviewType ReviewType
procurementId String? // reference to procurement if applicable
isAnonymous Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
buyer Buyer @relation(fields: [buyerId], references: [id])
farmer Farmer @relation(fields: [farmerId], references: [id])
@@index([buyerId])
@@index([farmerId])
@@index([reviewType])
@@index([rating])
@@index([procurementId])
@@index([createdAt])
@@map("buyer_reviews")
}

View File

@ -1,284 +0,0 @@
// Procurement & Trading Domain
// Contains Procurement, Contract, Harvest, and related trading models
// Procurement-specific Enums
enum ProcurementStatus {
PENDING
QUALITY_ASSESSMENT
APPROVED
REJECTED
IN_TRANSIT
DELIVERED
INVOICED
PAID
COMPLETED
CANCELLED
PARTIALLY_REJECTED
}
enum AgreementType {
PURCHASE_AGREEMENT
SUPPLY_CONTRACT
EXCLUSIVE_SUPPLY
SEASONAL_CONTRACT
FORWARD_CONTRACT
SPOT_CONTRACT
CONSIGNMENT
PARTNERSHIP
}
enum ContractStatus {
DRAFT
PENDING_REVIEW
PENDING_SIGNATURE
ACTIVE
FULFILLED
EXPIRED
TERMINATED
CANCELLED
BREACH
RENEWED
}
enum ProcurementAttachmentType {
PRODUCT_PHOTO
QUALITY_ASSESSMENT_PHOTO
WEIGHING_PHOTO
PACKAGING_PHOTO
LOADING_PHOTO
DELIVERY_PHOTO
CONTRACT_DOCUMENT
INVOICE
RECEIPT
PAYMENT_PROOF
QUALITY_CERTIFICATE
TRANSPORT_DOCUMENT
REJECTION_PHOTO
SIGNATURE_DOCUMENT
OTHER_DOCUMENT
}
// Procurement Models
model Harvest {
id String @id @default(cuid())
farmerId String
farmId String
plotId String?
variantId String
quantity Decimal // in kg
harvestDate DateTime
qualityGrade QualityGrade
waterContent Decimal? // percentage
density Decimal? // kg/m3
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer @relation(fields: [farmerId], references: [id])
farm Farm @relation(fields: [farmId], references: [id])
plot Plot? @relation(fields: [plotId], references: [id])
variant ProductVariant @relation(fields: [variantId], references: [id])
procurement Procurement?
modifiers HarvestModifier[]
@@index([farmerId, farmId])
@@index([harvestDate])
@@index([variantId])
@@index([qualityGrade])
@@index([plotId])
@@map("harvests")
}
model Procurement {
id String @id @default(cuid())
procurementCode String @unique
farmerId String
buyerId String?
harvestId String @unique
variantId String
// Quantity and quality details
quantity Decimal // in kg
qualityGrade QualityGrade
waterContent Decimal? // percentage
density Decimal? // kg/m3
// Pricing details
basePrice Decimal // price per kg
premiumRate Decimal @default(0) // percentage
totalPrice Decimal
transportCost Decimal? @default(0)
processingCost Decimal? @default(0)
finalAmount Decimal // total amount to be paid
// Location and logistics
pickupLocation String?
deliveryLocation String?
pickupDate DateTime?
deliveryDate DateTime?
transportMethod String? // truck, motorcycle, etc.
// Quality assessment
assessedBy String? // quality assessor ID
assessmentDate DateTime?
assessmentNotes String?
rejectedQuantity Decimal? @default(0)
rejectionReason String?
// Payment details
paymentMethod PaymentMethod?
paymentReference String?
bankAccount String?
// Status and tracking
status ProcurementStatus @default(PENDING)
procurementDate DateTime @default(now())
approvedDate DateTime?
approvedBy String? // admin ID who approved
paymentDate DateTime?
completedDate DateTime?
// Documentation
contractId String? // link to formal contract
contractNumber String?
invoiceNumber String?
receiptNumber String?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer @relation(fields: [farmerId], references: [id])
buyer Buyer? @relation(fields: [buyerId], references: [id])
harvest Harvest @relation(fields: [harvestId], references: [id])
variant ProductVariant @relation(fields: [variantId], references: [id])
contract Contract? @relation(fields: [contractId], references: [id])
attachments ProcurementAttachment[]
@@index([procurementCode])
@@index([farmerId, buyerId])
@@index([status])
@@index([procurementDate])
@@index([variantId])
@@index([qualityGrade])
@@index([paymentMethod])
@@map("procurements")
}
model Contract {
id String @id @default(cuid())
contractNumber String @unique
farmerId String
buyerId String
productId String?
variantId String?
// Contract terms
title String
description String?
contractType AgreementType @default(PURCHASE_AGREEMENT)
// Pricing and quantity
agreedPrice Decimal // price per unit
minimumQuantity Decimal?
maximumQuantity Decimal?
totalValue Decimal?
unit String @default("kg")
// Quality specifications
qualityGrade QualityGrade?
qualityRequirements String?
// Timeline
startDate DateTime
endDate DateTime
deliverySchedule String? // delivery frequency/schedule
// Payment terms
paymentTerms String? // payment conditions
paymentMethod PaymentMethod?
advancePayment Decimal? @default(0)
advancePercentage Decimal? @default(0)
// Legal and compliance
terms String? // full terms and conditions
penalties String? // penalty clauses
forcemajeure String? // force majeure clause
governingLaw String? // applicable law
// Status and tracking
status ContractStatus @default(DRAFT)
signedDate DateTime?
signedByFarmer Boolean @default(false)
signedByBuyer Boolean @default(false)
farmerSignature String? // signature data or URL
buyerSignature String? // signature data or URL
witnessName String?
witnessSignature String?
// Performance tracking
totalDelivered Decimal? @default(0)
totalPaid Decimal? @default(0)
deliveryCount Int @default(0)
// Renewal and amendments
renewalDate DateTime?
amendmentCount Int @default(0)
parentContractId String? // for contract renewals
// Documentation
documentUrl String? // contract document
attachmentUrls String[] // supporting documents
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer @relation(fields: [farmerId], references: [id])
buyer Buyer @relation(fields: [buyerId], references: [id])
product Product? @relation(fields: [productId], references: [id])
variant ProductVariant? @relation(fields: [variantId], references: [id])
parentContract Contract? @relation("ContractRenewal", fields: [parentContractId], references: [id])
renewalContracts Contract[] @relation("ContractRenewal")
procurements Procurement[] // deliveries under this contract
@@index([contractNumber])
@@index([farmerId, buyerId])
@@index([status])
@@index([startDate, endDate])
@@index([productId, variantId])
@@index([signedDate])
@@map("contracts")
}
model ProcurementAttachment {
id String @id @default(cuid())
procurementId String
type ProcurementAttachmentType
title String
description String?
fileUrl String
fileName String
fileSize Int? // in bytes
mimeType String?
uploadedBy String? // user ID who uploaded
takenDate DateTime? // when photo was taken
gpsLocation Json? // GeoJSON point where photo was taken
isVerified Boolean @default(false)
verifiedBy String? // admin ID who verified
verifiedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
procurement Procurement @relation(fields: [procurementId], references: [id], onDelete: Cascade)
@@index([procurementId])
@@index([type])
@@index([isVerified])
@@index([createdAt])
@@map("procurement_attachments")
}

View File

@ -1,169 +0,0 @@
// Product Management Domain
// Contains Product, ProductVariant, modifiers, and pricing models
// Product-specific Enums
enum ModifierSelectionType {
DROPDOWN
INPUT
}
enum NominalType {
NOMINAL
PERCENTAGE
}
enum RuleCondition {
EQUALS
LESS_THAN
GREATER_THAN
BETWEEN
}
// Product Models
model Product {
id String @id @default(cuid())
name String // e.g., "Pepper"
code String @unique
description String?
category String
unit String @default("kg") // kg, ton, etc
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
plots Plot[]
variants ProductVariant[]
modifiers ProductModifier[]
contracts Contract[]
priceHistory PriceHistory[]
marketPrices MarketPrice[]
marketDemand MarketDemand[]
plotSeasons PlotSeason[]
seasonHarvests SeasonHarvest[]
@@index([code])
@@index([category])
@@index([isActive])
@@index([name])
@@map("products")
}
model ProductVariant {
id String @id @default(cuid())
productId String
name String // e.g., "Black Pepper", "White Pepper"
code String @unique
description String?
basePrice Decimal? // default base price
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
plots Plot[]
harvests Harvest[]
procurements Procurement[]
contracts Contract[]
priceHistory PriceHistory[]
marketPrices MarketPrice[]
marketDemand MarketDemand[]
plotSeasons PlotSeason[]
seasonHarvests SeasonHarvest[]
@@index([code])
@@index([productId])
@@index([isActive])
@@index([basePrice])
@@map("product_variants")
}
model ProductModifier {
id String @id @default(cuid())
productId String
name String // e.g., "Water Content", "Density"
code String
description String?
selectionType ModifierSelectionType @default(INPUT)
nominalType NominalType @default(NOMINAL)
options String[] // For dropdown selections
minimum Decimal?
maximum Decimal?
unit String? // %, kg/m3, etc
isRequired Boolean @default(false)
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
rules ProductModifierRule[]
harvestModifiers HarvestModifier[]
@@unique([productId, code])
@@index([productId])
@@index([isActive])
@@index([selectionType])
@@map("product_modifiers")
}
model ProductModifierRule {
id String @id @default(cuid())
modifierId String
condition RuleCondition
priceAdjustment Decimal // amount to adjust price
value String? // For equals condition
minValue Decimal? // For lessThan or between conditions
maxValue Decimal? // For greaterThan or between conditions
description String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
modifier ProductModifier @relation(fields: [modifierId], references: [id], onDelete: Cascade)
@@map("product_modifier_rules")
}
model HarvestModifier {
id String @id @default(cuid())
harvestId String
modifierId String
value String // actual value applied
adjustment Decimal? // calculated price adjustment
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
harvest Harvest @relation(fields: [harvestId], references: [id], onDelete: Cascade)
modifier ProductModifier @relation(fields: [modifierId], references: [id])
@@unique([harvestId, modifierId])
@@map("harvest_modifiers")
}
model PriceHistory {
id String @id @default(cuid())
productId String?
variantId String?
qualityGrade QualityGrade
basePrice Decimal
marketPrice Decimal?
premiumRate Decimal @default(0)
effectiveDate DateTime
region String?
notes String?
createdAt DateTime @default(now())
// Relations
product Product? @relation(fields: [productId], references: [id])
variant ProductVariant? @relation(fields: [variantId], references: [id])
@@index([productId, variantId])
@@index([effectiveDate])
@@index([qualityGrade])
@@index([region])
@@map("price_history")
}

View File

@ -1,76 +0,0 @@
// Reference Data Domain
// Contains normalized reference tables for countries, currencies, education, religion
model EducationLevel {
id String @id @default(cuid())
name String @unique
level Int // 0=No formal, 1=Elementary, etc.
description String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmers Farmer[]
@@index([name])
@@index([level])
@@index([isActive])
@@map("education_levels")
}
model Currency {
id String @id // ISO currency code like "idr", "usd"
name String @unique
code String @unique // ISO 4217 currency code
symbol String // ₹, $, Rp, etc.
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
financialRecords FinancialRecord[]
@@index([code])
@@index([isActive])
@@index([name])
@@map("currencies")
}
model Country {
id String @id // ISO country code like "indonesia", "malaysia"
name String @unique
code String @unique // ISO 3166-1 alpha-2 code
region String?
continent String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmerNationalities Farmer[] @relation("FarmerNationality")
farmerCountries Farmer[] @relation("FarmerCountry")
@@index([code])
@@index([region])
@@index([continent])
@@index([isActive])
@@index([name])
@@map("countries")
}
model Religion {
id String @id @default(cuid())
name String @unique
description String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmers Farmer[]
@@index([name])
@@index([isActive])
@@map("religions")
}

View File

@ -1,196 +0,0 @@
// Season & Cycle Management Domain
// Contains Season planning, PlotSeason tracking, and seasonal analytics
// Season-specific Enums
enum SeasonType {
WET_SEASON
DRY_SEASON
TRANSITION
YEAR_ROUND
SPRING
SUMMER
FALL
WINTER
}
enum PlantingStatus {
PLANNED
PLANTED
GROWING
FLOWERING
FRUITING
HARVESTING
HARVESTED
FAILED
ABANDONED
}
// Season Models
model Season {
id String @id @default(cuid())
name String // e.g., "Wet Season 2024", "Dry Season 2024"
seasonType SeasonType
year Int
// Timeline
startDate DateTime
endDate DateTime
// Weather characteristics
avgRainfall Decimal? // mm
avgTemperature Float? // celsius
avgHumidity Decimal? // percentage
// Agricultural phases
plantingStart DateTime?
plantingEnd DateTime?
growingStart DateTime?
growingEnd DateTime?
harvestStart DateTime?
harvestEnd DateTime?
// Region and scope
region String?
province String?
country String @default("Indonesia")
// Crop recommendations
recommendedCrops String[] // suitable crops for this season
notRecommendedCrops String[] // crops to avoid
// Market expectations
expectedDemand DemandLevel?
priceOutlook PriceTrend?
marketNotes String?
// Agricultural activities
activities Json? // structured data for farming activities
// Status
isActive Boolean @default(true)
isCurrent Boolean @default(false)
description String?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
plots PlotSeason[]
harvests SeasonHarvest[]
weatherData SeasonWeather[]
@@unique([seasonType, year, region])
@@index([seasonType, year])
@@index([region])
@@index([startDate, endDate])
@@index([isActive])
@@index([isCurrent])
@@map("seasons")
}
model PlotSeason {
id String @id @default(cuid())
plotId String
seasonId String
productId String?
variantId String?
// Planting details
plantedDate DateTime?
plantedArea Decimal? // hectares actually planted
seedVariety String?
// Expected outcomes
expectedYield Decimal? // kg per hectare
expectedHarvest Decimal? // total kg expected
expectedHarvestDate DateTime?
// Actual outcomes
actualYield Decimal? // kg per hectare
actualHarvest Decimal? // total kg harvested
actualHarvestDate DateTime?
status PlantingStatus @default(PLANNED)
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
plot Plot @relation(fields: [plotId], references: [id])
season Season @relation(fields: [seasonId], references: [id])
product Product? @relation(fields: [productId], references: [id])
variant ProductVariant? @relation(fields: [variantId], references: [id])
@@unique([plotId, seasonId])
@@index([plotId, seasonId])
@@index([status])
@@index([plantedDate])
@@index([productId, variantId])
@@map("plot_seasons")
}
model SeasonHarvest {
id String @id @default(cuid())
seasonId String
farmerId String
productId String
variantId String?
totalQuantity Decimal
averageQuality QualityGrade
totalValue Decimal
harvestCount Int @default(1)
firstHarvest DateTime
lastHarvest DateTime?
notes String?
createdAt DateTime @default(now())
// Relations
season Season @relation(fields: [seasonId], references: [id])
farmer Farmer @relation(fields: [farmerId], references: [id])
product Product @relation(fields: [productId], references: [id])
variant ProductVariant? @relation(fields: [variantId], references: [id])
@@index([seasonId, farmerId])
@@index([productId, variantId])
@@index([firstHarvest])
@@index([averageQuality])
@@map("season_harvests")
}
model SeasonWeather {
id String @id @default(cuid())
seasonId String
region String
// Aggregated weather data
totalRainfall Decimal? // mm
avgTemperature Float? // celsius
minTemperature Float? // celsius
maxTemperature Float? // celsius
avgHumidity Decimal? // percentage
// Extreme events
droughtDays Int? @default(0)
floodDays Int? @default(0)
stormCount Int? @default(0)
// Impact assessment
cropDamage Decimal? // percentage
yieldImpact Decimal? // percentage change
notes String?
createdAt DateTime @default(now())
// Relations
season Season @relation(fields: [seasonId], references: [id])
@@unique([seasonId, region])
@@index([seasonId, region])
@@index([totalRainfall])
@@index([avgTemperature])
@@map("season_weather")
}

View File

@ -1,119 +0,0 @@
// Training & Certification Domain
// Contains training programs, certifications, and farmer development
// Training-specific Enums
enum TrainingStatus {
ENROLLED
IN_PROGRESS
COMPLETED
DROPPED
}
enum CertificationStatus {
PENDING
APPROVED
REJECTED
EXPIRED
}
// Training Models
model Training {
id String @id @default(cuid())
title String
description String?
content String?
category String
duration Int? // in minutes
level String? // beginner, intermediate, advanced
prerequisites String?
maxParticipants Int? // Maximum number of participants
createdBy String? // Admin who created the training
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmerTrainings FarmerTraining[]
@@index([category])
@@index([level])
@@index([isActive])
@@index([duration])
@@index([createdBy])
@@map("trainings")
}
model FarmerTraining {
id String @id @default(cuid())
farmerId String
trainingId String
status TrainingStatus @default(ENROLLED)
progress Int @default(0) // percentage
score Float?
completedAt DateTime?
assignedBy String? // Admin who assigned the farmer to training
assignedAt DateTime? // When the assignment was made
assignmentReason String? // Reason for assignment
deadline DateTime? // Expected completion deadline
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer @relation(fields: [farmerId], references: [id], onDelete: Cascade)
training Training @relation(fields: [trainingId], references: [id], onDelete: Cascade)
@@unique([farmerId, trainingId])
@@index([farmerId])
@@index([trainingId])
@@index([status])
@@index([completedAt])
@@index([progress])
@@index([assignedBy])
@@index([assignedAt])
@@index([deadline])
@@map("farmer_trainings")
}
model Certification {
id String @id @default(cuid())
name String
description String?
validityPeriod Int? // in months
requirements String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmerCertifications FarmerCertification[]
@@index([name])
@@index([isActive])
@@index([validityPeriod])
@@map("certifications")
}
model FarmerCertification {
id String @id @default(cuid())
farmerId String
certificationId String
status CertificationStatus @default(PENDING)
issuedDate DateTime?
expiryDate DateTime?
certificateNumber String?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer @relation(fields: [farmerId], references: [id], onDelete: Cascade)
certification Certification @relation(fields: [certificationId], references: [id], onDelete: Cascade)
@@unique([farmerId, certificationId])
@@index([farmerId])
@@index([certificationId])
@@index([status])
@@index([expiryDate])
@@index([issuedDate])
@@map("farmer_certifications")
}

View File

@ -1,261 +0,0 @@
// User Management Domain
// Contains User, Farmer, Buyer, Administrator models and related enums
// User Status Enums
enum FarmerStatus {
ACTIVE
INACTIVE
SUSPENDED
PENDING_VERIFICATION
BLACKLISTED
}
enum BuyerStatus {
ACTIVE
INACTIVE
SUSPENDED
PENDING_VERIFICATION
BLACKLISTED
}
enum BuyerType {
INDIVIDUAL
WHOLESALER
RETAILER
PROCESSOR
EXPORTER
COOPERATIVE
GOVERNMENT
RESTAURANT
HOTEL
}
enum BusinessSize {
MICRO
SMALL
MEDIUM
LARGE
ENTERPRISE
}
enum LandOwnership {
OWNER
TENANT
SHARECROPPER
COOPERATIVE_MEMBER
GOVERNMENT_LEASE
FAMILY_LAND
OTHER
}
// User Models
model User {
id String @id @default(cuid())
email String @unique
phone String?
name String
role UserRole
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farmer Farmer?
buyer Buyer?
administrator Administrator?
notifications NotificationRecipient[]
@@map("users")
}
model Farmer {
id String @id @default(cuid())
userId String @unique
farmerCode String @unique
name String
phone String?
email String?
dateOfBirth DateTime?
gender Gender?
maritalStatus MaritalStatus?
spouseName String?
numberOfChildren Int? @default(0)
religionId String?
religion Religion? @relation(fields: [religionId], references: [id])
nationalityId String? @default("indonesia")
nationality Country? @relation("FarmerNationality", fields: [nationalityId], references: [id])
address String?
village String?
district String?
province String?
postalCode String?
countryId String? @default("indonesia")
country Country? @relation("FarmerCountry", fields: [countryId], references: [id])
// Location data
latitude Float?
longitude Float?
addressGeoJson Json? // GeoJSON point for precise location
// Identity information
identityType IdentityType?
identityNumber String?
identityExpiry DateTime?
// Banking information
bankAccount String?
bankName String?
accountHolderName String?
// Photos and attachments
profilePhotoUrl String?
idCardFrontUrl String?
idCardBackUrl String?
// Verification status
isVerified Boolean @default(false)
verificationDate DateTime?
verifiedBy String? // admin ID who verified
verificationNotes String?
// Farming experience
farmingExperience Int? // years of experience
educationLevelId String?
educationLevel EducationLevel? @relation(fields: [educationLevelId], references: [id])
occupation String? // primary occupation if not full-time farmer
monthlyIncome Decimal? // estimated monthly income
landOwnership LandOwnership?
primaryCrop String?
farmingMethods String[] // organic, conventional, etc.
hasVehicle Boolean? // for transportation
vehicleType String? // motorcycle, car, truck, etc.
hasSmartphone Boolean? @default(true)
internetAccess Boolean? @default(true)
// Emergency contact
emergencyContactName String?
emergencyContactPhone String?
emergencyContactRelation String?
// Status and metadata
status FarmerStatus @default(ACTIVE)
notes String?
joinedAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
farms Farm[]
trainings FarmerTraining[]
certifications FarmerCertification[]
procurements Procurement[]
harvests Harvest[]
attachments FarmerAttachment[]
workers FarmWorker[]
laborRecords LaborRecord[]
laborSchedules LaborSchedule[]
equipment Equipment[]
assets Asset[]
financialRecords FinancialRecord[]
reviews BuyerReview[]
contracts Contract[]
seasonHarvests SeasonHarvest[]
@@index([farmerCode])
@@index([userId])
@@index([status])
@@index([joinedAt])
@@index([latitude, longitude])
@@map("farmers")
}
model Buyer {
id String @id @default(cuid())
userId String @unique
buyerCode String @unique
name String
company String?
phone String?
email String?
address String?
// Enhanced buyer details
buyerType BuyerType?
businessLicense String?
taxNumber String?
contactPerson String?
contactPersonPhone String?
paymentTerms Int? // days
creditLimit Decimal?
preferredProducts String[]
qualityRequirements String?
// Location and logistics
warehouseAddress String?
deliveryPreference String?
operatingRadius Decimal? // km
// Business details
businessSize BusinessSize?
yearEstablished Int?
annualVolume Decimal? // estimated annual purchase volume
employeeCount Int?
website String?
// Financial information
bankAccount String?
bankName String?
accountHolderName String?
// Verification and status
isVerified Boolean @default(false)
verificationDate DateTime?
verifiedBy String? // admin ID who verified
verificationNotes String?
status BuyerStatus @default(ACTIVE)
// Photos and documents
profilePhotoUrl String?
businessLicenseUrl String?
taxCertificateUrl String?
// Preferences
preferredPickupDays String[] // Mon, Tue, Wed, etc.
preferredPickupTime String?
minimumOrderQuantity Decimal?
maximumOrderQuantity Decimal?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
procurements Procurement[]
reviews BuyerReview[]
contracts Contract[]
@@index([buyerCode])
@@index([userId])
@@index([status])
@@index([buyerType])
@@map("buyers")
}
model Administrator {
id String @id @default(cuid())
userId String @unique
adminCode String @unique
name String
phone String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([adminCode])
@@index([userId])
@@map("administrators")
}

View File

@ -1,66 +0,0 @@
// Weather & Climate Domain
// Contains weather data, forecasts, and climate tracking
model WeatherData {
id String @id @default(cuid())
farmId String?
plotId String?
latitude Float
longitude Float
date DateTime
temperature Float? // celsius
humidity Float? // percentage
rainfall Float? // mm
windSpeed Float? // km/h
windDirection String? // N, NE, E, SE, S, SW, W, NW
pressure Float? // hPa
uvIndex Float?
visibility Float? // km
weatherCondition String? // sunny, cloudy, rainy, etc.
dataSource String? // manual, weather_station, api
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
farm Farm? @relation(fields: [farmId], references: [id])
plot Plot? @relation(fields: [plotId], references: [id])
@@index([farmId, plotId])
@@index([latitude, longitude])
@@index([date])
@@index([weatherCondition])
@@index([dataSource])
@@index([temperature])
@@index([rainfall])
@@map("weather_data")
}
model WeatherForecast {
id String @id @default(cuid())
farmId String?
plotId String?
latitude Float
longitude Float
forecastDate DateTime
minTemperature Float?
maxTemperature Float?
humidity Float?
rainfall Float?
windSpeed Float?
weatherCondition String?
confidence Float? // percentage
source String
createdAt DateTime @default(now())
// Relations
farm Farm? @relation(fields: [farmId], references: [id])
plot Plot? @relation(fields: [plotId], references: [id])
@@index([farmId, plotId])
@@index([latitude, longitude])
@@index([forecastDate])
@@index([weatherCondition])
@@index([source])
@@index([confidence])
@@map("weather_forecasts")
}