diff --git a/rancangan_schema/schema.prisma b/rancangan_schema/schema.prisma new file mode 100644 index 0000000..ad131c8 --- /dev/null +++ b/rancangan_schema/schema.prisma @@ -0,0 +1,2663 @@ +// 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[] + + @@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. + 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) + identities FarmerIdentity[] + bankAccounts FarmerBankAccount[] + vehicles FarmerVehicle[] + 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 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 + 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") +} + +// 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, 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") +} + +// 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") +}