feat: Introduce comprehensive training and user management schemas
- Added Training, FarmerTraining, and Certification models to manage training programs and certifications for farmers. - Implemented User, Farmer, Buyer, and Administrator models to enhance user management and roles within the platform. - Established relationships between training and user models to track participation and progress. - Included enums for training and certification statuses to standardize state management. - Enhanced documentation for the new schema structure to improve maintainability and collaboration.
This commit is contained in:
parent
dacae9e70e
commit
2c7294a75b
|
|
@ -969,32 +969,89 @@ This comprehensive database schema is designed for a modern agricultural technol
|
|||
## Training & Certification
|
||||
|
||||
### Training
|
||||
**Purpose**: Farmer education and skill development
|
||||
**Purpose**: Admin-managed training program system for farmer skill development
|
||||
**Key Features**:
|
||||
- Course content management
|
||||
- Difficulty level classification
|
||||
- Duration tracking
|
||||
- Category organization
|
||||
- Administrative training program management
|
||||
- Course content and curriculum design
|
||||
- Participant capacity and prerequisites management
|
||||
- Admin assignment and selection workflows
|
||||
|
||||
**Course Management**:
|
||||
- Structured learning content
|
||||
- Beginner to advanced levels
|
||||
- Time-based progression
|
||||
- Category-based organization
|
||||
**Administrative Management**:
|
||||
- Training program creation and maintenance by admins
|
||||
- Content management with multimedia support
|
||||
- Participant capacity limits and prerequisite requirements
|
||||
- Admin-controlled activation and deactivation
|
||||
|
||||
**Course Structure**:
|
||||
- Structured learning content with modules and assessments
|
||||
- Difficulty level classification (beginner to advanced)
|
||||
- Duration tracking and scheduling
|
||||
- Category-based organization and filtering
|
||||
|
||||
**Assignment System**:
|
||||
- Admin-driven farmer selection and assignment
|
||||
- Capacity management and participant limits
|
||||
- Assignment reason tracking and documentation
|
||||
- Notification workflow for selected farmers
|
||||
|
||||
**Fields**:
|
||||
- `id`: Unique training program identifier
|
||||
- `title`: Training program name
|
||||
- `description`: Detailed program description
|
||||
- `content`: Training materials and curriculum
|
||||
- `category`: Subject area classification
|
||||
- `duration`: Training duration in minutes
|
||||
- `level`: Difficulty level (beginner/intermediate/advanced)
|
||||
- `prerequisites`: Required knowledge or certifications
|
||||
- `maxParticipants`: Maximum number of participants
|
||||
- `createdBy`: Admin who created the training program
|
||||
- `isActive`: Program availability status
|
||||
- `createdAt`, `updatedAt`: Timestamps
|
||||
|
||||
**Relations**: Connected to farmer training assignments and admin management
|
||||
|
||||
### FarmerTraining
|
||||
**Purpose**: Individual training progress and achievement tracking
|
||||
**Purpose**: Admin-assigned training participation and progress tracking
|
||||
**Key Features**:
|
||||
- Enrollment and progress tracking
|
||||
- Score and completion management
|
||||
- Certificate generation
|
||||
- Learning analytics
|
||||
- Admin-controlled farmer assignment system
|
||||
- Training progress and completion tracking
|
||||
- Assignment reason and deadline management
|
||||
- Notification workflow for assignments
|
||||
|
||||
**Assignment Management**:
|
||||
- Admin assignment with reason documentation
|
||||
- Assignment date and deadline tracking
|
||||
- Notification system for assigned farmers
|
||||
- Assignment audit trail and history
|
||||
|
||||
**Progress Tracking**:
|
||||
- Enrollment status and progress percentage
|
||||
- Score tracking and assessments
|
||||
- Completion certificates
|
||||
- Learning path optimization
|
||||
- Score tracking and assessment results
|
||||
- Completion certificates and achievements
|
||||
- Learning analytics and performance metrics
|
||||
|
||||
**Assignment Workflow**:
|
||||
- Admin selects training program
|
||||
- Admin filters and selects suitable farmers
|
||||
- System creates assignments with notifications
|
||||
- Farmers receive assignment notifications
|
||||
- Progress tracking through completion
|
||||
|
||||
**Fields**:
|
||||
- `id`: Unique assignment identifier
|
||||
- `farmerId`: Assigned farmer reference
|
||||
- `trainingId`: Training program reference
|
||||
- `status`: Training status (ENROLLED, IN_PROGRESS, COMPLETED, DROPPED)
|
||||
- `progress`: Completion percentage (0-100)
|
||||
- `score`: Assessment score
|
||||
- `completedAt`: Completion timestamp
|
||||
- `assignedBy`: Admin who made the assignment
|
||||
- `assignedAt`: Assignment creation date
|
||||
- `assignmentReason`: Reason for farmer selection
|
||||
- `deadline`: Expected completion date
|
||||
- `createdAt`, `updatedAt`: Timestamps
|
||||
|
||||
**Relations**: Connected to farmers, training programs, and admin management
|
||||
|
||||
### Certification
|
||||
**Purpose**: Professional certification and credential management
|
||||
|
|
@ -1002,7 +1059,24 @@ This comprehensive database schema is designed for a modern agricultural technol
|
|||
- Certification program management
|
||||
- Validity period tracking
|
||||
- Requirements documentation
|
||||
- Industry recognition
|
||||
- Industry recognition and standards
|
||||
|
||||
**Certification Management**:
|
||||
- Certification program definition and maintenance
|
||||
- Validity period and renewal requirements
|
||||
- Industry standard compliance
|
||||
- Quality assurance and recognition
|
||||
|
||||
**Fields**:
|
||||
- `id`: Unique certification identifier
|
||||
- `name`: Certification name
|
||||
- `description`: Certification details and requirements
|
||||
- `validityPeriod`: Certification validity in months
|
||||
- `requirements`: Qualification requirements
|
||||
- `isActive`: Certification program status
|
||||
- `createdAt`, `updatedAt`: Timestamps
|
||||
|
||||
**Relations**: Connected to farmer certifications and training programs
|
||||
|
||||
### FarmerCertification
|
||||
**Purpose**: Individual certification status and management
|
||||
|
|
@ -1010,7 +1084,26 @@ This comprehensive database schema is designed for a modern agricultural technol
|
|||
- Certification status tracking
|
||||
- Issue and expiry date management
|
||||
- Certificate number assignment
|
||||
- Renewal notifications
|
||||
- Renewal notifications and workflow
|
||||
|
||||
**Certification Tracking**:
|
||||
- Application and approval workflow
|
||||
- Certificate generation and numbering
|
||||
- Expiry date monitoring and renewal alerts
|
||||
- Certification verification and validation
|
||||
|
||||
**Fields**:
|
||||
- `id`: Unique certification record identifier
|
||||
- `farmerId`: Certified farmer reference
|
||||
- `certificationId`: Certification program reference
|
||||
- `status`: Certification status (PENDING, APPROVED, REJECTED, EXPIRED)
|
||||
- `issuedDate`: Certificate issue date
|
||||
- `expiryDate`: Certificate expiry date
|
||||
- `certificateNumber`: Unique certificate number
|
||||
- `notes`: Additional certification notes
|
||||
- `createdAt`, `updatedAt`: Timestamps
|
||||
|
||||
**Relations**: Connected to farmers and certification programs
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -1158,6 +1251,10 @@ High-volume counters use `BigInt`:
|
|||
- `PestDiseaseType`: PEST, DISEASE, WEED, NUTRIENT_DEFICIENCY, etc.
|
||||
- `SeverityLevel`: LOW, MEDIUM, HIGH, CRITICAL, CATASTROPHIC
|
||||
|
||||
**Training & Certification**:
|
||||
- `TrainingStatus`: ENROLLED, IN_PROGRESS, COMPLETED, DROPPED
|
||||
- `CertificationStatus`: PENDING, APPROVED, REJECTED, EXPIRED
|
||||
|
||||
**Business Operations**:
|
||||
- `ProcurementStatus`: PENDING to COMPLETED (11 stages)
|
||||
- `PaymentMethod`: CASH, BANK_TRANSFER, MOBILE_MONEY, etc.
|
||||
|
|
|
|||
2228
schema.prisma
2228
schema.prisma
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,214 @@
|
|||
# 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.
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
// 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
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,439 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -0,0 +1,518 @@
|
|||
# Training System User Stories
|
||||
|
||||
## 📚 **Epic: Farmer Training & Development Platform**
|
||||
|
||||
### **Theme: Skill Development & Certification**
|
||||
|
||||
---
|
||||
|
||||
## **👨🌾 As a Farmer:**
|
||||
|
||||
### **Training Participation**
|
||||
|
||||
#### **User Story 1: View Assigned Training Programs**
|
||||
```gherkin
|
||||
Feature: View and participate in assigned training programs
|
||||
|
||||
Scenario: View assigned training programs
|
||||
Given I am logged into the farmer dashboard
|
||||
When I navigate to the training section
|
||||
Then I should see training programs assigned to me by admin:
|
||||
- Training title and description
|
||||
- Duration and schedule
|
||||
- Level and prerequisites
|
||||
- Assignment date and deadline
|
||||
And I should see training status (ENROLLED, IN_PROGRESS, COMPLETED)
|
||||
And I can view detailed training information
|
||||
```
|
||||
|
||||
#### **User Story 2: Receive Training Assignment Notification**
|
||||
```gherkin
|
||||
Scenario: Receive notification when assigned to training
|
||||
Given an admin has assigned me to a training program
|
||||
When the assignment is created
|
||||
Then I should receive a notification stating:
|
||||
- Training program name
|
||||
- Assignment reason
|
||||
- Training schedule and duration
|
||||
- Expected completion date
|
||||
And the training should appear in my "My Trainings" dashboard
|
||||
And my status should be set to "ENROLLED"
|
||||
```
|
||||
|
||||
### **Training Progress & Learning**
|
||||
|
||||
#### **User Story 3: Access Training Content**
|
||||
```gherkin
|
||||
Feature: Complete training and track progress
|
||||
|
||||
Scenario: Access training content
|
||||
Given I am enrolled in "Integrated Pest Management" training
|
||||
When I click "Start Learning"
|
||||
Then I should see the training content modules:
|
||||
- Video lessons
|
||||
- Reading materials
|
||||
- Interactive quizzes
|
||||
- Practical exercises
|
||||
And my progress should be tracked as I complete sections
|
||||
And I can bookmark important content for later reference
|
||||
```
|
||||
|
||||
#### **User Story 4: Complete Training Modules**
|
||||
```gherkin
|
||||
Scenario: Complete training modules progressively
|
||||
Given I am in progress with a training program
|
||||
When I complete each module
|
||||
Then my progress percentage should update automatically
|
||||
And I should see my completion status on the dashboard
|
||||
And next modules should unlock sequentially
|
||||
When I complete all modules with 80% average score
|
||||
Then my status should change to "COMPLETED"
|
||||
And I should be eligible for certification
|
||||
And I should receive a completion certificate
|
||||
```
|
||||
|
||||
#### **User Story 5: Track Learning Progress**
|
||||
```gherkin
|
||||
Scenario: Monitor my learning journey
|
||||
Given I have multiple trainings in progress
|
||||
When I view my training dashboard
|
||||
Then I should see:
|
||||
- Overall progress percentage for each training
|
||||
- Time spent learning this week
|
||||
- Upcoming deadlines or recommendations
|
||||
- My skill level improvements
|
||||
- Badges or achievements earned
|
||||
```
|
||||
|
||||
### **Certification Management**
|
||||
|
||||
#### **User Story 6: Apply for Certification**
|
||||
```gherkin
|
||||
Feature: Earn and manage certifications
|
||||
|
||||
Scenario: Apply for certification after training
|
||||
Given I have completed "Organic Farming Basics" training
|
||||
And my final score is above 85%
|
||||
When I click "Apply for Certification"
|
||||
Then my certification status should be "PENDING"
|
||||
And an admin should be notified for review
|
||||
And I should see estimated review time (3-5 business days)
|
||||
And I can upload additional supporting documents if required
|
||||
```
|
||||
|
||||
#### **User Story 7: View My Certifications**
|
||||
```gherkin
|
||||
Scenario: Manage my certification portfolio
|
||||
Given I have earned certifications
|
||||
When I visit my profile certification section
|
||||
Then I should see all my certifications with:
|
||||
- Certificate name and issuing body
|
||||
- Issue date and expiry date
|
||||
- Digital certificate download link (PDF)
|
||||
- Verification QR code
|
||||
- Renewal requirements and timeline
|
||||
And I should get notifications 30 days before expiry
|
||||
And I can share certifications with buyers or cooperatives
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **🏢 As a Buyer:**
|
||||
|
||||
### **Farmer Qualification Verification**
|
||||
|
||||
#### **User Story 8: Verify Farmer Qualifications**
|
||||
```gherkin
|
||||
Feature: Verify farmer qualifications during procurement
|
||||
|
||||
Scenario: View farmer certifications during procurement
|
||||
Given I am evaluating a farmer for procurement
|
||||
When I view their profile
|
||||
Then I should see their completed trainings and certifications:
|
||||
- Organic farming certifications
|
||||
- Pest management training completion
|
||||
- Quality standards training
|
||||
- Safety and compliance certifications
|
||||
And I can verify certificate authenticity via QR codes
|
||||
And I can see their training scores and completion dates
|
||||
```
|
||||
|
||||
#### **User Story 9: Filter Farmers by Certifications**
|
||||
```gherkin
|
||||
Scenario: Find qualified farmers for specific requirements
|
||||
Given I need organic certified produce for export
|
||||
When I search for farmers in the procurement section
|
||||
Then I can filter by:
|
||||
- Certification type (Organic, GAP, Halal, etc.)
|
||||
- Certification validity status
|
||||
- Training completion in specific areas
|
||||
- Skill level and experience
|
||||
And see only farmers meeting my quality requirements
|
||||
And view their certification verification status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **👨💼 As an Administrator:**
|
||||
|
||||
### **Authentication & Access**
|
||||
|
||||
#### **User Story 10: Admin Login**
|
||||
```gherkin
|
||||
Feature: Admin authentication and access control
|
||||
|
||||
Scenario: Admin login to training management system
|
||||
Given I am an authorized administrator
|
||||
When I navigate to the admin login page
|
||||
And I enter my valid credentials
|
||||
Then I should be logged into the admin dashboard
|
||||
And I should see training management options
|
||||
And I should have access to all training-related features
|
||||
```
|
||||
|
||||
### **Training Content Management**
|
||||
|
||||
#### **User Story 11: Create Training Programs**
|
||||
```gherkin
|
||||
Feature: Create and manage training programs
|
||||
|
||||
Scenario: Create comprehensive training program
|
||||
Given I am logged into the admin training management dashboard
|
||||
When I create a new training program
|
||||
Then I should specify:
|
||||
- Title: "Sustainable Pepper Cultivation"
|
||||
- Description and learning objectives
|
||||
- Category: Crop-specific training
|
||||
- Skill level: Beginner/Intermediate/Advanced
|
||||
- Duration: 180 minutes
|
||||
- Prerequisites: Basic farming knowledge
|
||||
- Training modules and content structure
|
||||
- Assessment criteria and passing score
|
||||
- Certification eligibility requirements
|
||||
And I can upload multimedia content (videos, PDFs, images)
|
||||
And set the training as active/inactive
|
||||
```
|
||||
|
||||
#### **User Story 12: Edit Training Programs**
|
||||
```gherkin
|
||||
Scenario: Edit existing training program
|
||||
Given I am viewing a training program "Organic Farming Basics"
|
||||
When I click "Edit Training"
|
||||
Then I should be able to modify:
|
||||
- Title and description
|
||||
- Category and skill level
|
||||
- Duration and prerequisites
|
||||
- Training content and modules
|
||||
- Assessment criteria
|
||||
- Active/inactive status
|
||||
And save the changes
|
||||
And notify assigned farmers of updates
|
||||
```
|
||||
|
||||
#### **User Story 13: Delete Training Programs**
|
||||
```gherkin
|
||||
Scenario: Delete training program
|
||||
Given I am viewing a training program that needs to be removed
|
||||
When I click "Delete Training"
|
||||
Then I should see a confirmation dialog
|
||||
And I should be warned if farmers are currently enrolled
|
||||
When I confirm deletion
|
||||
Then the training should be removed from the system
|
||||
And enrolled farmers should be notified of cancellation
|
||||
```
|
||||
|
||||
#### **User Story 14: View All Training Programs**
|
||||
```gherkin
|
||||
Scenario: View comprehensive training program list
|
||||
Given I am in the admin training management dashboard
|
||||
When I navigate to the training programs section
|
||||
Then I should see all training programs with:
|
||||
- Title and category
|
||||
- Status (active/inactive)
|
||||
- Number of enrolled farmers
|
||||
- Creation and last modified dates
|
||||
- Completion statistics
|
||||
And I can filter by category, status, or date
|
||||
And I can sort by various criteria
|
||||
```
|
||||
|
||||
#### **User Story 15: View Individual Training Program**
|
||||
```gherkin
|
||||
Scenario: View detailed training program information
|
||||
Given I am viewing the training programs list
|
||||
When I click on a specific training program
|
||||
Then I should see detailed information:
|
||||
- Complete training description and objectives
|
||||
- All modules and content structure
|
||||
- Assessment criteria and requirements
|
||||
- List of enrolled farmers with their progress
|
||||
- Completion statistics and analytics
|
||||
- Farmer feedback and ratings
|
||||
And I can edit or delete the training from this view
|
||||
```
|
||||
|
||||
### **Farmer Assignment Management**
|
||||
|
||||
#### **User Story 16: Select Farmers for Training**
|
||||
```gherkin
|
||||
Feature: Assign farmers to training programs
|
||||
|
||||
Scenario: Assign farmers to training program
|
||||
Given I am viewing a training program "Integrated Pest Management"
|
||||
When I click "Assign Farmers"
|
||||
Then I should see a list of available farmers with:
|
||||
- Farmer name and location
|
||||
- Crop types and farm details
|
||||
- Previous training history
|
||||
- Skill level and certifications
|
||||
And I can filter farmers by:
|
||||
- Location/region
|
||||
- Crop type
|
||||
- Skill level
|
||||
- Previous training completion
|
||||
And I can select multiple farmers
|
||||
And I can add assignment notes/reasons
|
||||
When I confirm assignment
|
||||
Then selected farmers should be enrolled in the training
|
||||
And farmers should receive assignment notifications
|
||||
```
|
||||
|
||||
#### **User Story 17: Monitor Training Effectiveness**
|
||||
```gherkin
|
||||
Scenario: Analyze training program performance
|
||||
Given training programs have been running for 3 months
|
||||
When I view training analytics dashboard
|
||||
Then I should see comprehensive metrics:
|
||||
- Assignment numbers per program and category
|
||||
- Completion rates by farmer demographics
|
||||
- Average scores and assessment results
|
||||
- Time spent per module
|
||||
- Farmer feedback and ratings
|
||||
- Drop-off points in training content
|
||||
- Geographic distribution of participants
|
||||
- ROI impact on farm productivity
|
||||
And I can export reports for stakeholders
|
||||
```
|
||||
|
||||
### **Certification Review & Approval**
|
||||
|
||||
#### **User Story 18: Process Certification Applications**
|
||||
```gherkin
|
||||
Feature: Review and approve certifications efficiently
|
||||
|
||||
Scenario: Review certification applications systematically
|
||||
Given farmers have applied for certifications
|
||||
When I access the certification review queue
|
||||
Then I should see pending applications with:
|
||||
- Farmer profile and farm details
|
||||
- Training completion records and scores
|
||||
- Assessment results and practical evaluations
|
||||
- Supporting documents and evidence
|
||||
- Previous certification history
|
||||
And I can sort by application date, urgency, or type
|
||||
And I can assign applications to specialist reviewers
|
||||
```
|
||||
|
||||
#### **User Story 19: Approve and Issue Certifications**
|
||||
```gherkin
|
||||
Scenario: Complete certification approval process
|
||||
Given I am reviewing a certification application for "Organic Farming"
|
||||
And the farmer has met all requirements:
|
||||
- Training completion with 85% score
|
||||
- Practical assessment passed
|
||||
- Supporting documentation verified
|
||||
When I approve the application
|
||||
Then the farmer's status should change to "APPROVED"
|
||||
And a digital certificate should be auto-generated with:
|
||||
- Unique certificate number
|
||||
- QR code for verification
|
||||
- Expiry date (24 months for organic certification)
|
||||
And the farmer should receive approval notification
|
||||
And the certificate should be available for download
|
||||
And the farmer's profile should reflect the new certification
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **📊 Advanced User Stories:**
|
||||
|
||||
### **Personalized Learning Paths**
|
||||
|
||||
#### **User Story 20: AI-Driven Training Recommendations**
|
||||
```gherkin
|
||||
Feature: Intelligent training recommendations
|
||||
|
||||
As a Farmer
|
||||
I want to receive personalized training recommendations
|
||||
So that I can improve my specific farming challenges
|
||||
|
||||
Scenario: Receive targeted recommendations based on farm data
|
||||
Given my farm profile shows:
|
||||
- Crop: Pepper cultivation
|
||||
- Recent issue: Pest damage reported
|
||||
- Skill level: Intermediate
|
||||
- Location: West Java
|
||||
When I view my dashboard
|
||||
Then I should see recommended trainings:
|
||||
- "Integrated Pest Management for Pepper" (High Priority)
|
||||
- "Organic Pest Control Methods" (Medium Priority)
|
||||
- "Beneficial Insects in Agriculture" (Low Priority)
|
||||
And recommendations should explain why they're relevant
|
||||
And I can see success stories from similar farmers
|
||||
```
|
||||
|
||||
### **Group Training & Community Learning**
|
||||
|
||||
#### **User Story 21: Collaborative Learning Sessions**
|
||||
```gherkin
|
||||
Feature: Group training and peer learning
|
||||
|
||||
As a Farmer
|
||||
I want to join group training sessions with other farmers
|
||||
So that I can learn collaboratively and share experiences
|
||||
|
||||
Scenario: Participate in regional group training
|
||||
Given there's a group training "Sustainable Rice Farming" in my district
|
||||
And 15 other farmers from nearby villages are enrolled
|
||||
When I join the group session
|
||||
Then I should be able to:
|
||||
- Participate in live discussions
|
||||
- Share my farming experiences
|
||||
- Ask questions to trainers and peers
|
||||
- Access group chat and resources
|
||||
- Schedule follow-up practice sessions
|
||||
And I can connect with other farmers for ongoing support
|
||||
```
|
||||
|
||||
### **Mobile Learning & Offline Access**
|
||||
|
||||
#### **User Story 22: Mobile-Optimized Learning**
|
||||
```gherkin
|
||||
Feature: Mobile learning with offline capabilities
|
||||
|
||||
As a Farmer
|
||||
I want to access training on my smartphone during field work
|
||||
So that I can learn flexibly without internet dependency
|
||||
|
||||
Scenario: Offline training access
|
||||
Given I have limited internet connectivity in my farm area
|
||||
When I download training content while connected
|
||||
Then I should be able to:
|
||||
- Access all downloaded materials offline
|
||||
- Complete quizzes and assessments
|
||||
- Take notes and bookmark content
|
||||
- Record my progress locally
|
||||
And when I reconnect to internet:
|
||||
- All progress should sync automatically
|
||||
- New content should be available for download
|
||||
- Notifications should be received
|
||||
```
|
||||
|
||||
### **Training Impact & ROI Tracking**
|
||||
|
||||
#### **User Story 23: Measure Training Effectiveness**
|
||||
```gherkin
|
||||
Feature: Training impact analytics
|
||||
|
||||
As an Administrator
|
||||
I want to track training impact on farm performance
|
||||
So that I can improve training programs and demonstrate ROI
|
||||
|
||||
Scenario: Analyze training return on investment
|
||||
Given farmers have completed productivity training over 6 months
|
||||
When I generate impact analytics reports
|
||||
Then I should see correlations between:
|
||||
- Training completion and yield improvements (% increase)
|
||||
- Certification status and product quality grades
|
||||
- Skill level progression and income increases
|
||||
- Training investment vs. productivity gains
|
||||
- Farmer satisfaction vs. knowledge retention
|
||||
- Regional performance vs. training participation rates
|
||||
And I can identify most effective training content
|
||||
And create evidence-based improvements
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **🎯 Success Metrics & KPIs:**
|
||||
|
||||
### **Engagement Metrics**
|
||||
- **Enrollment Rate**: 75% of active farmers enroll in at least one training annually
|
||||
- **Completion Rate**: 80% of enrolled farmers complete their training programs
|
||||
- **Retention Rate**: 90% of farmers continue learning after first training
|
||||
- **Mobile Usage**: 60% of training consumption happens on mobile devices
|
||||
|
||||
### **Learning Effectiveness**
|
||||
- **Knowledge Retention**: 85% average score on post-training assessments
|
||||
- **Practical Application**: 70% of farmers implement learned techniques
|
||||
- **Certification Rate**: 60% of completed trainings lead to certifications
|
||||
- **Skill Progression**: 50% improvement in skill assessments
|
||||
|
||||
### **Business Impact**
|
||||
- **Productivity Improvement**: 25% average yield increase post-training
|
||||
- **Quality Enhancement**: 30% improvement in product quality grades
|
||||
- **Income Growth**: 20% increase in farmer income within 12 months
|
||||
- **Certification Value**: 15% premium for certified produce
|
||||
|
||||
### **User Satisfaction**
|
||||
- **Training Quality Rating**: 4.5/5 average rating
|
||||
- **Content Relevance**: 85% find training directly applicable
|
||||
- **Platform Usability**: 90% satisfaction with mobile app experience
|
||||
- **Support Quality**: 95% satisfaction with help and guidance
|
||||
|
||||
---
|
||||
|
||||
## **📱 Technical Implementation Considerations:**
|
||||
|
||||
### **Platform Requirements**
|
||||
- **Mobile-First Design**: Responsive design optimized for smartphones
|
||||
- **Offline Capability**: Progressive Web App (PWA) with offline content
|
||||
- **Multi-language Support**: Bahasa Indonesia, Javanese, and English
|
||||
- **Low Bandwidth**: Optimized for 2G/3G networks in rural areas
|
||||
|
||||
### **Content Management**
|
||||
- **Rich Media Support**: Videos, interactive presentations, PDF documents
|
||||
- **Progressive Content**: Modular structure with prerequisite management
|
||||
- **Assessment Engine**: Quizzes, practical evaluations, peer assessments
|
||||
- **Analytics Integration**: Detailed learning analytics and progress tracking
|
||||
|
||||
### **Integration Features**
|
||||
- **Farm Data Integration**: Connect training to actual farm performance
|
||||
- **Certification Verification**: QR codes and blockchain verification
|
||||
- **Payment Integration**: Premium training content and certification fees
|
||||
- **Communication Tools**: Chat, forums, and mentorship connections
|
||||
|
||||
### **Security & Compliance**
|
||||
- **Data Privacy**: GDPR compliance for farmer personal data
|
||||
- **Certificate Security**: Tamper-proof digital certificates
|
||||
- **Content Protection**: DRM for premium training content
|
||||
- **Access Control**: Role-based permissions for different user types
|
||||
|
||||
---
|
||||
|
||||
## **🚀 Implementation Roadmap:**
|
||||
|
||||
### **Phase 1: Core Training Platform (Months 1-2)**
|
||||
- Basic training content management
|
||||
- Farmer enrollment and progress tracking
|
||||
- Mobile-responsive interface
|
||||
- Basic certification workflow
|
||||
|
||||
### **Phase 2: Enhanced Learning Experience (Months 3-4)**
|
||||
- Offline content access
|
||||
- Interactive assessments and quizzes
|
||||
- Group training capabilities
|
||||
- Advanced progress analytics
|
||||
|
||||
### **Phase 3: Intelligent Features (Months 5-6)**
|
||||
- AI-driven personalized recommendations
|
||||
- Training impact tracking and ROI analysis
|
||||
- Integration with farm management data
|
||||
- Advanced certification verification
|
||||
|
||||
### **Phase 4: Community & Ecosystem (Months 7-8)**
|
||||
- Peer learning and mentorship
|
||||
- Marketplace integration for certified farmers
|
||||
- Third-party training provider integration
|
||||
- Advanced analytics and reporting
|
||||
Loading…
Reference in New Issue