// 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 } // 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 } enum VehicleType { MOTORCYCLE CAR TRUCK PICKUP VAN TRACTOR BICYCLE BOAT OTHER } // User Models model User { id String @id @default(cuid()) email String @unique password String 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[] createdFarmers Farmer[] @relation("FarmerCreatedBy") updatedFarmers Farmer[] @relation("FarmerUpdatedBy") @@map("users") } model Farmer { id String @id @default(cuid()) userId String @unique farmerCode String @unique //YYMMDDHHMM + '000001' 16 digits #sequence number name String phone String? email String? birthPlace String? dateOfBirth DateTime? gender Gender? maritalStatus MaritalStatus? spouseName String? familySize 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? subDistrict 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 // Photos and attachments profilePhotoUrl String? // Khusus buat KTP 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 farmingStartDate DateTime? // when they started farming educationLevelId String? educationLevel EducationLevel? @relation(fields: [educationLevelId], references: [id]) // SD, SMP, SMA, D3, S1, etc. occupation String? // primary occupation if not full-time farmer monthlyIncome Decimal? // estimated monthly income landOwnership LandOwnership? // Kepemilikan lahan tempat tinggal primaryCrop String? farmingMethods String[] // organic, conventional, etc. 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 createdBy String? // User ID who created this farmer record updatedBy String? // User ID who last updated this farmer record // Relations user User @relation(fields: [userId], references: [id], onDelete: Cascade) creator User? @relation("FarmerCreatedBy", fields: [createdBy], references: [id]) updater User? @relation("FarmerUpdatedBy", fields: [updatedBy], references: [id]) identities FarmerIdentity[] bankAccounts FarmerBankAccount[] vehicles FarmerVehicle[] farms Farm[] trainings FarmerTraining[] certifications FarmerCertification[] trainingInvitations TrainingInvitation[] trainingAttendances TrainingAttendance[] 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 FarmerIdentity { id String @id @default(cuid()) farmerId String identityType IdentityType identityNumber String identityExpiry DateTime? issuedBy String? // issuing authority issuedDate DateTime? isPrimary Boolean @default(false) // mark one identity as primary isVerified Boolean @default(false) verifiedBy String? // admin ID who verified verifiedAt DateTime? notes String? // Document attachments frontPhotoUrl String? backPhotoUrl String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Relations farmer Farmer @relation(fields: [farmerId], references: [id], onDelete: Cascade) @@unique([farmerId, identityType, identityNumber]) @@index([farmerId]) @@index([identityType]) @@index([identityNumber]) @@index([isPrimary]) @@index([isVerified]) @@map("farmer_identities") } model FarmerBankAccount { id String @id @default(cuid()) farmerId String accountNumber String accountHolderName String bankName String bankCode String? // bank's SWIFT/routing code branchName String? branchCode String? accountType String? // savings, checking, etc. currency String @default("IDR") isPrimary Boolean @default(false) // mark one account as primary isActive Boolean @default(true) isVerified Boolean @default(false) verifiedBy String? // admin ID who verified verifiedAt DateTime? notes String? // Additional details openedDate DateTime? monthlyLimit Decimal? // transaction limit createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Relations farmer Farmer @relation(fields: [farmerId], references: [id], onDelete: Cascade) @@unique([farmerId, accountNumber, bankCode]) @@index([farmerId]) @@index([accountNumber]) @@index([bankName]) @@index([isPrimary]) @@index([isActive]) @@index([isVerified]) @@map("farmer_bank_accounts") } model FarmerVehicle { id String @id @default(cuid()) farmerId String vehicleType VehicleType brand String? model String? year Int? licensePlate String? registrationNumber String? color String? condition String? // excellent, good, fair, poor isPrimary Boolean @default(false) // mark one vehicle as primary isActive Boolean @default(true) isOwned Boolean @default(true) // owned vs rented/borrowed // Usage and capacity capacity String? // passenger capacity or cargo capacity fuelType String? // petrol, diesel, electric, hybrid // Documentation registrationExpiry DateTime? insuranceExpiry DateTime? lastMaintenance DateTime? nextMaintenance DateTime? // Purchase/rental details purchaseDate DateTime? purchasePrice Decimal? currentValue Decimal? monthlyPayment Decimal? // for financed vehicles notes String? photoUrls String[] // photos of the vehicle createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Relations farmer Farmer @relation(fields: [farmerId], references: [id], onDelete: Cascade) @@index([farmerId]) @@index([vehicleType]) @@index([licensePlate]) @@index([isPrimary]) @@index([isActive]) @@index([isOwned]) @@map("farmer_vehicles") } 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") } // 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") } // 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 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") } // 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") } // 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") } // 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") } // 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") } // 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") } // 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") } // 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") } // Training & Certification Domain // Contains training programs, certifications, farmer development, and group training management // Features: batch training, invitation system, attendance tracking, location management, documentation // Training-specific Enums enum TrainingStatus { ENROLLED // Farmer enrolled in training program IN_PROGRESS // Currently participating in training COMPLETED // Successfully completed training DROPPED // Dropped out of training program } enum TrainingLocationType { VILLAGE_HALL // Training held at village hall/balai desa FARM_FIELD // Field training at farm/kebun FARMER_HOME // Training at farmer's home COOPERATIVE // Training at cooperative office GOVERNMENT_OFFICE // Training at government facility ONLINE // Virtual/online training OTHER // Other location type } enum AttendanceStatus { INVITED // Farmer invited to training CONFIRMED // Farmer confirmed attendance ATTENDED // Farmer attended the training ABSENT // Farmer was absent CANCELLED // Invitation/attendance cancelled } enum CertificationStatus { PENDING APPROVED REJECTED EXPIRED } // Training Models model Training { id String @id @default(cuid()) title String // Training title/name description String? // Training description content String? // Training content/materials category String // Training category (e.g., "Pest Management", "Organic Farming") duration Int? // Training duration in minutes level String? // Training level: beginner, intermediate, advanced prerequisites String? // Prerequisites for joining training maxParticipants Int? // Maximum number of participants allowed createdBy String? // Admin ID who created the training // Training schedule and location management scheduledDate DateTime? // Date when training is scheduled startTime DateTime? // Training start time endTime DateTime? // Training end time locationType TrainingLocationType? // Type of training location location String? // Specific location name/description locationAddress String? // Full address of training location locationGPS Json? // GeoJSON point for precise location coordinates // Group/batch management for farmer groups batchName String? // Name identifier for the training batch targetGroup String? // Description of target audience/group kelompokTaniIds String[] // Array of farmer group IDs (SH/kelompok tani) participating isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Relations farmerTrainings FarmerTraining[] // Individual farmer training records trainingInvitations TrainingInvitation[] // Training invitations sent to farmers trainingAttendances TrainingAttendance[] // Attendance records for the training trainingDocuments TrainingDocument[] // Photos, videos, and documents related to training @@index([category]) @@index([level]) @@index([isActive]) @@index([duration]) @@index([createdBy]) @@index([scheduledDate]) @@index([locationType]) @@index([batchName]) @@map("trainings") } 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 TrainingInvitation { id String @id @default(cuid()) trainingId String // Reference to Training farmerId String // Reference to Farmer being invited invitedBy String? // Admin ID who created the invitation invitedAt DateTime @default(now()) // When invitation was sent message String? // Custom invitation message for the farmer // Response tracking - farmer's response to invitation responseStatus AttendanceStatus @default(INVITED) // Current status of farmer's response respondedAt DateTime? // When farmer responded to invitation responseNote String? // Farmer's note/comment when responding // Reminder system for follow-up reminderSent Boolean @default(false) // Whether reminder has been sent reminderSentAt DateTime? // When last reminder was sent reminderCount Int @default(0) // Number of reminders sent createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Relations training Training @relation(fields: [trainingId], references: [id], onDelete: Cascade) farmer Farmer @relation(fields: [farmerId], references: [id], onDelete: Cascade) @@unique([trainingId, farmerId]) // One invitation per farmer per training @@index([trainingId]) @@index([farmerId]) @@index([responseStatus]) @@index([invitedAt]) @@index([reminderSent]) @@map("training_invitations") } model TrainingAttendance { id String @id @default(cuid()) trainingId String // Reference to Training farmerId String // Reference to Farmer attendanceStatus AttendanceStatus @default(INVITED) // Attendance status // Check-in/out tracking system for training sessions checkInTime DateTime? // When farmer checked in to training checkOutTime DateTime? // When farmer checked out of training checkInBy String? // Admin ID who recorded the check-in checkOutBy String? // Admin ID who recorded the check-out // Participation evaluation and feedback participationScore Float? // 1-5 rating of farmer's participation quality completedTasks Boolean @default(false) // Whether farmer completed training tasks feedback String? // Farmer's feedback about the training experience notes String? // Additional notes about attendance createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Relations training Training @relation(fields: [trainingId], references: [id], onDelete: Cascade) farmer Farmer @relation(fields: [farmerId], references: [id], onDelete: Cascade) @@unique([trainingId, farmerId]) // One attendance record per farmer per training @@index([trainingId]) @@index([farmerId]) @@index([attendanceStatus]) @@index([checkInTime]) @@index([participationScore]) @@map("training_attendances") } model TrainingDocument { id String @id @default(cuid()) trainingId String // Reference to Training title String // Document title/name description String? // Document description fileUrl String // URL/path to the file fileName String // Original filename fileSize Int? // File size in bytes mimeType String? // File MIME type (image/jpeg, video/mp4, etc.) // Document type and categorization documentType String? // Type: photo, video, certificate, handout, presentation, etc. category String? // Category: documentation, material, certificate, evaluation // Metadata and tracking information uploadedBy String? // User ID who uploaded the document takenBy String? // Person who took the photo/video (photographer/videographer) takenAt DateTime? // When the photo/video was taken during training gpsLocation Json? // GeoJSON point coordinates where photo/video was taken // Verification and quality control isVerified Boolean @default(false) // Whether document has been verified by admin verifiedBy String? // Admin ID who verified the document verifiedAt DateTime? // When document was verified // Access control isPublic Boolean @default(false) // Whether document is visible to training participants createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Relations training Training @relation(fields: [trainingId], references: [id], onDelete: Cascade) @@index([trainingId]) @@index([documentType]) @@index([category]) @@index([isVerified]) @@index([isPublic]) @@index([uploadedBy]) @@index([takenAt]) @@map("training_documents") } model Certification { id String @id @default(cuid()) name String 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") } // 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") } // 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") } // 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") } // 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") }