/** * * **Current State:** `BlockService` imports and initializes `PhaseRepository` to perform cross-entity validation checking if a `Phase` exists before creating or updating a `Block`. * * **Intended Mutation:** Remove `PhaseRepository` from imports, properties, `getRepos()`, `create()`, and `update()` methods to unbind Phase and Block entities. * * **Risk Mitigation:** Cross-entity validation is entirely shifted to the `FFB Production` ledger level, making the master block seeder 100% independent. */ import { Injectable, NotFoundException, Inject, forwardRef } from '@nestjs/common'; import { FFBLangChainService } from '../../FFB/services/ffb-langchain.service'; import { MongoCoreService } from '../../mongo/mongo-core.service'; import { BlockRepository } from '../repositories/block.repository'; import { Block } from '../schemas/site.schema'; import { FFBProductionService } from '../../FFB/services/ffb-production.service'; @Injectable() export class BlockService { private repo: BlockRepository; constructor( private readonly mongoCore: MongoCoreService, @Inject(forwardRef(() => FFBProductionService)) private readonly ffbService: FFBProductionService, @Inject(forwardRef(() => FFBLangChainService)) private readonly ffbLangChainService: FFBLangChainService, ) { } private async getRepos() { if (!this.repo) { const db = await this.mongoCore.getDb(); this.repo = new BlockRepository(db); await this.repo.init(); } return { repo: this.repo }; } async create(block: Block): Promise { const { repo } = await this.getRepos(); return repo.create(block); } async findAll(filter: Record = {}): Promise { const { repo } = await this.getRepos(); return repo.findAll(filter); } async findById(id: string): Promise { const { repo } = await this.getRepos(); return repo.findById(id); } async update(id: string, update: Partial): Promise { const { repo } = await this.getRepos(); const block = await repo.findById(id); if (!block) throw new NotFoundException('Block not found'); await repo.update(id, update); } async delete(id: string): Promise { const { repo } = await this.getRepos(); const block = await repo.findById(id); if (!block) throw new NotFoundException('Block not found'); // Cascading Delete: // 1. Delete all FFB Production records referencing this block await this.ffbService.deleteMany({ blockCode: block.blockCode }); // 2. Delete the block itself await repo.delete(id); } async generateDescription(data: { name: string, phaseName?: string, size?: number, numOfTrees?: number }): Promise<{ description: string }> { const prompt = `Write a realistic, professional description (2–4 sentences) for an oil palm plantation block named "${data.name}"${data.phaseName ? ` within phase "${data.phaseName}"` : ''}.${data.size ? ` It has a size of ${data.size} units` : ''}${data.numOfTrees ? ` and contains ${data.numOfTrees} trees` : ''}. Choose 1-2 aspects from the following to incorporate naturally: - Block health and maturity (e.g., trees at peak production age, healthy canopy development, consistent fruit set) - Field accessibility (e.g., easily accessible by tractor paths, slightly sloped area requiring specialized harvesting tools) - Environmental features (e.g., bordering primary forest, presence of natural irrigation channels, rich nutrient-holding soil) - Monitoring and care (e.g., subject to intensive pest monitoring, recently fertilized, under strict irrigation schedule) Guidelines: - Maintain a grounded, operational tone. - Do not mention the bullet points explicitly; weave them into the narrative. - Ensure the length is exactly 2-4 sentences. Description:`; const description = await this.ffbLangChainService.chatStateless(prompt); return { description: description.replace(/^Description:\s*/i, '').trim() }; } }