Refactor nitrogen balance calculations from farm-level to field-level#407
Conversation
…ance on farm level
🦋 Changeset detectedLatest commit: c8ec1c3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
WalkthroughRefactors balances to a field-first model: adds cached per-field calculators, threads deposition into field inputs, converts Decimal results to numeric variants, updates fdm-app integrations/routes/components to use field/farm helpers, and extends caching with redaction support. Changes
Sequence Diagram(s)sequenceDiagram
participant App as fdm-app (route/component)
participant Integr as Integrations (calculator exports)
participant FarmCalc as Farm aggregator (calculateNitrogenBalance / calculateOrganicMatterBalance)
participant FieldCalc as Cached per-field calc (getNitrogenBalanceField / getOrganicMatterBalanceField)
participant Cache as withCalculationCache
participant Fdm as FdmType (data layer)
App->>Integr: request field or farm balance
Integr->>FarmCalc: invoke aggregator (fdm, timeframe, ...)
FarmCalc->>Fdm: prefetch shared data (e.g., deposition)
FarmCalc->>FieldCalc: call per-field calc (fieldInput...)
FieldCalc->>Cache: compute/check cache key (uses redaction if configured)
Cache-->>FieldCalc: hit / miss
alt cache miss
FieldCalc->>Fdm: fetch per-field data (cultivations, harvests, soilAnalyses, fertilizers)
FieldCalc->>FieldCalc: compute numeric field balance
FieldCalc->>Cache: store result
end
FieldCalc-->>FarmCalc: return field result (numeric)
FarmCalc->>FarmCalc: aggregate field results → farm result
FarmCalc-->>Integr: return farm result
Integr-->>App: return requested data
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## development #407 +/- ##
===============================================
+ Coverage 87.10% 87.61% +0.51%
===============================================
Files 90 90
Lines 4380 4391 +11
Branches 1347 1354 +7
===============================================
+ Hits 3815 3847 +32
+ Misses 565 544 -21
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
fdm-calculator/src/balance/nitrogen/removal/residue.test.ts (1)
292-299: Inconsistent test data: Missing required cultivation fields.All other test cases in this file were updated to include
b_lu_nameandb_lu_croprotationfields in cultivation objects, but this test case was not updated. This creates an inconsistency and will likely cause a type error if these fields are now required in theFieldInput["cultivations"]type.🔧 Add missing fields to maintain consistency
const cultivations: FieldInput["cultivations"] = [ { b_lu: "cultivation1", b_lu_catalogue: "catalogue2", b_lu_start: new Date("2022-01-01"), b_lu_end: new Date("2022-12-31"), m_cropresidue: true, + b_lu_name: "Cultivation 1", + b_lu_croprotation: "other", }, ]fdm-calculator/src/balance/nitrogen/removal/harvest.test.ts (1)
9-15: Critical: Fix type error by adding missing required fields.The pipeline failure indicates that this cultivation object is missing the required
b_lu_nameandb_lu_croprotationfields. Other test cases in this file (lines 82-83, 198-199, 311-312) have correctly included these fields.🐛 Proposed fix
{ b_lu: "cultivation1", b_lu_catalogue: "catalogue1", m_cropresidue: true, b_lu_start: null, b_lu_end: null, + b_lu_name: "Cultivation 1", + b_lu_croprotation: "cereal", },fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx (1)
329-506: Fix inconsistent balance access pattern.Lines 330-339 correctly extract and guard
balance, but lines 449-452 and 462-465 still directly accessresolvedNitrogenBalance.balance.balanceandresolvedNitrogenBalance.balance.target, bypassing the guard. This creates an inconsistency and potential null reference issues.♻️ Proposed fix to use the extracted balance consistently
<span className="text-xl font-bold text-right whitespace-nowrap px-2"> {Math.round( - resolvedNitrogenBalance - .balance - .balance, + balance.balance, )}{" "} kg N </span> </div> <div className="grid grid-cols-[1fr_auto] items-center"> <p className="whitespace-nowrap px-2"> Streefwaarde </p> <span className="font-semibold text-right whitespace-nowrap px-2"> {Math.round( - resolvedNitrogenBalance - .balance - .target, + balance.target, )}{" "} kg N </span>
🤖 Fix all issues with AI agents
In @fdm-app/app/integrations/calculator.ts:
- Around line 107-117: The code calls getOrganicMatterBalanceField and accesses
fields[0] without ensuring fields has at least one element; add the same
defensive check used in the nitrogen balance function: validate that fields is
an array with length > 0 (and that fields[0].field exists) before calling
getOrganicMatterBalanceField or building the return object, and if the check
fails, throw or return an error/empty result consistently (refer to symbols
getOrganicMatterBalanceField, organicMatterBalanceResult, fields, fieldResult,
b_id) to avoid out-of-bounds access.
- Around line 50-58: The code accesses fields[0] and fields[0].field.b_area
without ensuring fields is non-empty; add a defensive check before calling
getNitrogenBalanceField and before reading b_area in the return object: verify
fields && fields.length > 0 (or handle an empty array case) and either return an
early error/empty result or use a safe default for fieldInput and b_area; update
the call site that constructs the argument object for getNitrogenBalanceField
and the return payload so they never dereference undefined (refer to
getNitrogenBalanceField, the local variable fields, and the b_area access).
In @fdm-calculator/src/balance/nitrogen/supply/fixation.test.ts:
- Line 138: The test data has a mismatch: the cultivation object sets
b_lu_croprotation to "other" while its linked catalogue (via b_lu_catalogue:
"catalogue2") declares the rotation as "grass"; update the cultivation fixture
so b_lu_croprotation matches the catalogue value ("grass") for the entry linked
by b_lu_catalogue "catalogue2" (or alternatively update the catalogue entry to
"other" if that is the intended source of truth), ensuring b_lu_croprotation and
the catalogue entry are consistent.
In @fdm-calculator/src/balance/nitrogen/target.test.ts:
- Around line 24-25: The test hardcodes b_lu_croprotation as "cereal" but some
test scenarios expect grass cultivations; update the test data so
b_lu_croprotation matches each scenario by parameterizing createCultivation
calls and the cultivations array used in calculateTargetForNitrogenBalance: pass
"grass" for tests that assert grass behavior (e.g., the cases creating "grass1")
and "cereal" for cereal cases (e.g., "crop1"), ensuring the cultivation input's
b_lu_croprotation field aligns with the cultivation details map and the
assertions.
In @fdm-calculator/src/balance/nitrogen/types.d.ts:
- Around line 95-104: Update the JSDoc for the NitrogenSupplyDeposition type to
correct the spelling: change "depostion" to "deposition" in the top comment so
it reads "atmospheric deposition"; ensure the rest of the comment (units and
description) remains unchanged and the type name NitrogenSupplyDeposition is
left intact.
🧹 Nitpick comments (3)
fdm-calculator/src/balance/nitrogen/emission/ammonia/residues.test.ts (1)
30-31: LGTM! Test data correctly augmented to support field-level input extensions.The addition of
b_lu_nameandb_lu_croprotationfields across all cultivation test objects aligns with the PR's refactoring objectives to support richer field-level inputs. The values are consistent and appropriate for the test scenarios.Optional: Extract hardcoded string constant
The string
"Cultivation 1"is repeated across 7 test cases. Consider extracting it as a constant at the file level for easier maintenance:+const TEST_CULTIVATION_NAME = "Cultivation 1" +const TEST_CROP_ROTATION = "cereal" + describe("calculateNitrogenEmissionViaAmmoniaByResidues", () => {Then reference it in test data:
b_lu_name: TEST_CULTIVATION_NAME, b_lu_croprotation: TEST_CROP_ROTATION,This would make future updates to test data easier if field values need to change.
Also applies to: 71-72, 140-141, 166-167, 205-206, 245-246, 295-296
fdm-calculator/src/balance/nitrogen/input.ts (1)
129-129: Add fallback value or document whydepositionSupplyis guaranteed to be defined.Line 129 calls
depositionByField.get(field.b_id), which has typeNitrogenSupplyDeposition | undefined, but theFieldInputtype requiresdepositionSupplyto be non-optional. Although the implementation guarantees a Map entry for every field infarmFields(the same array passed tocalculateAllFieldsNitrogenSupplyByDeposition), TypeScript cannot verify this contractually.Either add a fallback:
depositionSupply: depositionByField.get(field.b_id) ?? { total: new Decimal(0) },Or add a comment explaining why the Map entry is guaranteed to exist.
fdm-calculator/src/balance/organic-matter/index.ts (1)
234-241: Numeric conversion approach differs from nitrogen balance.The organic matter aggregation uses
.round().toNumber()directly on individual values, while nitrogen balance usesconvertDecimalToNumberRecursiveon the entire object. Both approaches work, but consider usingconvertDecimalToNumberRecursivehere for consistency:♻️ Optional: Consistent conversion approach
- return { - balance: avgFarmBalance.round().toNumber(), - supply: avgFarmSupply.round().toNumber(), - degradation: avgFarmDegradation.round().toNumber(), - fields: fieldsWithBalanceResults, - hasErrors, - fieldErrorMessages, - } + return convertDecimalToNumberRecursive({ + balance: avgFarmBalance, + supply: avgFarmSupply, + degradation: avgFarmDegradation, + fields: fieldsWithBalanceResults, + hasErrors, + fieldErrorMessages, + }) as OrganicMatterBalanceNumeric
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (30)
fdm-app/app/components/blocks/balance/nitrogen-chart.tsxfdm-app/app/components/blocks/balance/nitrogen-details.tsxfdm-app/app/components/blocks/fertilizer-applications/metrics.tsxfdm-app/app/integrations/calculator.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-calculator/src/balance/nitrogen/emission/ammonia/fertilizers.test.tsfdm-calculator/src/balance/nitrogen/emission/ammonia/residues.test.tsfdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.tsfdm-calculator/src/balance/nitrogen/index.test.tsfdm-calculator/src/balance/nitrogen/index.tsfdm-calculator/src/balance/nitrogen/input.test.tsfdm-calculator/src/balance/nitrogen/input.tsfdm-calculator/src/balance/nitrogen/performance.test.tsfdm-calculator/src/balance/nitrogen/removal/harvest.test.tsfdm-calculator/src/balance/nitrogen/removal/index.test.tsfdm-calculator/src/balance/nitrogen/removal/residue.test.tsfdm-calculator/src/balance/nitrogen/supply/deposition.test.tsfdm-calculator/src/balance/nitrogen/supply/deposition.tsfdm-calculator/src/balance/nitrogen/supply/fixation.test.tsfdm-calculator/src/balance/nitrogen/supply/mineralization.test.tsfdm-calculator/src/balance/nitrogen/target.test.tsfdm-calculator/src/balance/nitrogen/types.d.tsfdm-calculator/src/balance/organic-matter/index.test.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-calculator/src/balance/organic-matter/types.tsfdm-calculator/src/balance/shared/conversion.tsfdm-calculator/src/index.ts
💤 Files with no reviewable changes (1)
- fdm-calculator/src/balance/nitrogen/performance.test.ts
🧰 Additional context used
🧠 Learnings (40)
📓 Common learnings
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 343
File: fdm-calculator/src/balance/organic-matter/types.d.ts:12-132
Timestamp: 2025-11-21T10:02:25.556Z
Learning: In the organic matter balance calculation system (fdm-calculator), degradation values are negative by definition. This means the balance calculation using supply.total.plus(degradation.total) is correct, as it effectively computes supply - |degradation|. This follows the same pattern as the nitrogen balance system.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 236
File: fdm-calculator/src/balance/nitrogen/index.ts:173-0
Timestamp: 2025-08-14T14:31:55.384Z
Learning: In nitrogen balance calculations for agricultural systems, the balance should only include ammonia emissions (emission.ammonia.total) and should not include nitrate leaching from the emission calculation. The nitrate component (emission.nitrate) should be excluded from the balance formula.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:162-168
Timestamp: 2025-05-26T10:32:00.674Z
Learning: In the nitrogen balance calculation system (fdm-calculator), removal and volatilization values are negative by definition. This means the balance calculation using supply.total.add(removal.total).add(volatilization.total) is correct, as it effectively computes supply - |removal| - |volatilization|.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 343
File: fdm-app/app/components/blocks/balance/organic-matter-chart.tsx:19-25
Timestamp: 2025-11-24T10:43:09.278Z
Learning: In the organic matter balance chart (fdm-app), degradation values are multiplied by -1 before plotting to show supply and degradation bars side-by-side with positive magnitudes, enabling direct visual comparison. This is a deliberate design choice for the organic matter chart visualization, different from the nitrogen balance chart which uses separate stacks.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.711Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 156
File: fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts:295-303
Timestamp: 2025-07-21T12:06:07.070Z
Learning: Functions in the fdm-calculator with "NL2025" in their names are specifically designed for Netherlands 2025 agricultural norms calculation and hardcoded 2025 dates are appropriate in this context, as different years would have separate calculation modules.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:236-238
Timestamp: 2025-05-26T10:32:15.538Z
Learning: In nitrogen balance calculations for agricultural systems, removal and volatilization are calculated as negative values by definition since they represent nitrogen losses from the system. The balance calculation uses addition (.add()) for all components because removal and volatilization are already negative, so adding them effectively subtracts the losses from the supply.
📚 Learning: 2025-08-14T14:31:55.384Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 236
File: fdm-calculator/src/balance/nitrogen/index.ts:173-0
Timestamp: 2025-08-14T14:31:55.384Z
Learning: In nitrogen balance calculations for agricultural systems, the balance should only include ammonia emissions (emission.ammonia.total) and should not include nitrate leaching from the emission calculation. The nitrate component (emission.nitrate) should be excluded from the balance formula.
Applied to files:
fdm-app/app/components/blocks/balance/nitrogen-details.tsxfdm-calculator/src/balance/nitrogen/emission/ammonia/residues.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/components/blocks/fertilizer-applications/metrics.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-calculator/src/index.tsfdm-calculator/src/balance/nitrogen/removal/index.test.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-12-15T12:19:47.858Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:47.858Z
Learning: When reviewing code, prefer storing only non-sensitive UI/state data in sessionStorage. For map viewState (e.g., longitude/latitude), ensure it represents non-personal business data and that persistence across sessions is justified, documented, and respects user privacy. If persisting, use a clearly scoped, namespaced key, guard access with try/catch, and avoid syncing with servers or exposing data to third-party scripts. Apply this guideline to all TSX files that manage client-side UI state.
Applied to files:
fdm-app/app/components/blocks/balance/nitrogen-details.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/components/blocks/fertilizer-applications/metrics.tsxfdm-app/app/components/blocks/balance/nitrogen-chart.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsx
📚 Learning: 2025-08-11T11:55:26.053Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/integrations/nmi.ts:54-0
Timestamp: 2025-08-11T11:55:26.053Z
Learning: The NMI API Estimates endpoint (`https://api.nmi-agro.nl/estimates`) always returns the fields `b_gwl_ghg`, `b_gwl_glg`, and `cultivations` according to its specification. These fields should be kept as required (not optional) in the TypeScript return type and Zod validation schema in `fdm-app/app/integrations/nmi.ts`.
Applied to files:
fdm-calculator/src/balance/nitrogen/supply/mineralization.test.tsfdm-calculator/src/balance/nitrogen/emission/ammonia/fertilizers.test.tsfdm-calculator/src/balance/nitrogen/input.test.tsfdm-calculator/src/balance/nitrogen/supply/deposition.test.tsfdm-calculator/src/balance/organic-matter/index.test.tsfdm-calculator/src/balance/nitrogen/emission/ammonia/residues.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/components/blocks/fertilizer-applications/metrics.tsxfdm-app/app/components/blocks/balance/nitrogen-chart.tsxfdm-calculator/src/balance/organic-matter/types.tsfdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-calculator/src/balance/nitrogen/removal/residue.test.tsfdm-calculator/src/balance/nitrogen/supply/fixation.test.tsfdm-calculator/src/balance/nitrogen/input.tsfdm-calculator/src/balance/nitrogen/target.test.tsfdm-calculator/src/balance/nitrogen/supply/deposition.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-calculator/src/balance/nitrogen/removal/harvest.test.tsfdm-calculator/src/index.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-calculator/src/balance/nitrogen/removal/index.test.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.tsfdm-calculator/src/balance/nitrogen/types.d.ts
📚 Learning: 2024-11-27T12:15:36.425Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-data/src/cultivations/index.test.ts:57-59
Timestamp: 2024-11-27T12:15:36.425Z
Learning: In `fdm-data/src/cultivations/index.test.ts`, the `fdm` object created by `drizzle` does not have an `.end()` method. Cleanup code should not attempt to call `fdm.end();`.
Applied to files:
fdm-calculator/src/balance/nitrogen/supply/mineralization.test.tsfdm-calculator/src/balance/nitrogen/emission/ammonia/fertilizers.test.tsfdm-calculator/src/balance/nitrogen/input.test.tsfdm-calculator/src/balance/nitrogen/emission/ammonia/residues.test.tsfdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.tsfdm-calculator/src/balance/nitrogen/supply/fixation.test.tsfdm-calculator/src/balance/nitrogen/target.test.tsfdm-calculator/src/balance/nitrogen/removal/harvest.test.tsfdm-calculator/src/balance/nitrogen/index.test.tsfdm-calculator/src/balance/nitrogen/removal/index.test.ts
📚 Learning: 2025-01-23T15:18:57.212Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-core/src/db/schema.ts:407-426
Timestamp: 2025-01-23T15:18:57.212Z
Learning: In the farm data model, each cultivation (b_lu) can have only one termination date but can have multiple harvest dates. This is enforced through the database schema where cultivationTerminating uses b_lu as primary key while cultivationHarvesting uses a composite primary key of b_id_harvestable and b_lu.
Applied to files:
fdm-calculator/src/balance/nitrogen/supply/mineralization.test.tsfdm-calculator/src/balance/nitrogen/emission/ammonia/fertilizers.test.tsfdm-calculator/src/balance/nitrogen/emission/ammonia/residues.test.tsfdm-calculator/src/balance/nitrogen/removal/residue.test.tsfdm-calculator/src/balance/nitrogen/supply/fixation.test.tsfdm-calculator/src/balance/nitrogen/removal/harvest.test.tsfdm-calculator/src/balance/nitrogen/index.test.tsfdm-calculator/src/balance/nitrogen/removal/index.test.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/types.d.ts
📚 Learning: 2025-08-13T10:33:05.313Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.
Applied to files:
fdm-calculator/src/balance/nitrogen/emission/ammonia/fertilizers.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-calculator/src/balance/nitrogen/input.tsfdm-calculator/src/balance/nitrogen/supply/deposition.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-calculator/src/index.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-calculator/src/balance/nitrogen/index.test.tsfdm-calculator/src/balance/nitrogen/removal/index.test.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-09-23T12:37:58.711Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.711Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.test.tsfdm-calculator/src/balance/nitrogen/supply/deposition.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-calculator/src/balance/nitrogen/input.tsfdm-calculator/src/balance/nitrogen/supply/deposition.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-calculator/src/balance/organic-matter/index.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-09-23T10:02:32.123Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.$p_id.tsx:151-164
Timestamp: 2025-09-23T10:02:32.123Z
Learning: The getFertilizer function from svenvw/fdm-core throws an exception if the fertilizer doesn't exist, rather than returning null or undefined.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-calculator/src/balance/organic-matter/index.tsfdm-calculator/src/balance/nitrogen/index.test.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-09-24T14:02:48.574Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.574Z
Learning: Both getFertilizer and getFertilizers functions in svenvw/fdm-core perform authorization checks using the user's principal_id to verify farm access before returning fertilizer data.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-09-24T14:02:48.574Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.574Z
Learning: The getFertilizer function in svenvw/fdm-core does not perform authorization checks, unlike getFertilizers which includes a checkPermission call to verify farm access. This means getFertilizer(fdm, p_id) can potentially return fertilizer details for any fertilizer ID without validating user permissions.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-04-04T14:27:39.518Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 116
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx:111-154
Timestamp: 2025-04-04T14:27:39.518Z
Learning: In the FDM application, cultivation retrieval logic should be centralized in utility functions rather than duplicated across loader and action functions to improve maintainability and ensure consistent behavior.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.test.tsfdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2024-11-27T11:27:27.797Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-data/src/cultivations/index.test.ts:57-59
Timestamp: 2024-11-27T11:27:27.797Z
Learning: When cleaning up test data in `afterAll` hooks in `fdm-data/src/cultivations/index.test.ts`, use `fdm.delete(schema.tableName).execute();` to delete rows from a table without dropping the table itself.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.test.tsfdm-calculator/src/balance/nitrogen/removal/harvest.test.ts
📚 Learning: 2025-09-26T08:34:50.413Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 279
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx:277-283
Timestamp: 2025-09-26T08:34:50.413Z
Learning: In the fdm project, fdm-core and fdm-app are updated together as part of a monorepo structure, which eliminates legacy data concerns when new fields like b_isproductive are introduced. Both packages are synchronized, so there's no need for defensive coding against undefined values for newly introduced database fields.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.test.tsfdm-calculator/src/balance/nitrogen/input.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-calculator/src/balance/organic-matter/index.tsfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-03-06T15:23:48.352Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 95
File: fdm-core/src/catalogues.ts:134-170
Timestamp: 2025-03-06T15:23:48.352Z
Learning: When writing tests for fdm-core, avoid using Vitest's `vi` mocking utilities and prefer manual JavaScript mocks.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.test.tsfdm-calculator/src/balance/nitrogen/index.test.ts
📚 Learning: 2025-03-06T14:58:48.603Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 95
File: fdm-core/src/cultivation.ts:67-73
Timestamp: 2025-03-06T14:58:48.603Z
Learning: When writing unit tests for the fdm project, avoid using Vitest's mocking utilities (vi) as it has caused problems in the past not related to the actual code. Instead, use simple object literals with methods that throw errors to test error handling.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.test.tsfdm-calculator/src/balance/nitrogen/index.test.ts
📚 Learning: 2025-03-06T14:38:52.315Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 95
File: fdm-core/src/cultivation.ts:67-73
Timestamp: 2025-03-06T14:38:52.315Z
Learning: When writing unit tests for the fdm project, avoid using vi.mock() as it has caused implementation problems in the past. Use direct mock functions with vi.fn() instead.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.test.tsfdm-calculator/src/balance/nitrogen/index.test.ts
📚 Learning: 2025-03-06T15:23:29.958Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 95
File: fdm-core/src/catalogues.ts:96-132
Timestamp: 2025-03-06T15:23:29.958Z
Learning: When writing tests for the FDM codebase, avoid using Vitest's `vi` mocking utilities (`vi.spyOn()`, etc.). Prefer using natural errors, real function behavior, and actual inputs that trigger expected failures.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.test.tsfdm-calculator/src/balance/nitrogen/index.test.ts
📚 Learning: 2025-03-06T15:20:37.653Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 95
File: fdm-core/src/catalogues.ts:12-50
Timestamp: 2025-03-06T15:20:37.653Z
Learning: Do not use vi (Vitest) mocking utilities in unit tests for the fdm project.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.test.tsfdm-calculator/src/balance/nitrogen/index.test.ts
📚 Learning: 2025-02-14T09:56:37.606Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 75
File: fdm-app/app/routes/farm.$b_id_farm.field.$b_id.fertilizer.tsx:68-71
Timestamp: 2025-02-14T09:56:37.606Z
Learning: The `calculateDose` function in `svenvw/fdm-calculator` is a synchronous function that includes built-in validation for negative application amounts and nutrient rates.
Applied to files:
fdm-calculator/src/balance/nitrogen/supply/deposition.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/components/blocks/fertilizer-applications/metrics.tsxfdm-app/app/components/blocks/balance/nitrogen-chart.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-calculator/src/balance/nitrogen/supply/deposition.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-calculator/src/index.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-calculator/src/balance/nitrogen/index.test.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-11-21T10:02:25.556Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 343
File: fdm-calculator/src/balance/organic-matter/types.d.ts:12-132
Timestamp: 2025-11-21T10:02:25.556Z
Learning: In the organic matter balance calculation system (fdm-calculator), degradation values are negative by definition. This means the balance calculation using supply.total.plus(degradation.total) is correct, as it effectively computes supply - |degradation|. This follows the same pattern as the nitrogen balance system.
Applied to files:
fdm-calculator/src/balance/nitrogen/supply/deposition.test.tsfdm-calculator/src/balance/organic-matter/index.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-calculator/src/index.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/types.d.ts
📚 Learning: 2025-05-26T10:32:00.674Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:162-168
Timestamp: 2025-05-26T10:32:00.674Z
Learning: In the nitrogen balance calculation system (fdm-calculator), removal and volatilization values are negative by definition. This means the balance calculation using supply.total.add(removal.total).add(volatilization.total) is correct, as it effectively computes supply - |removal| - |volatilization|.
Applied to files:
fdm-calculator/src/balance/nitrogen/supply/deposition.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/components/blocks/fertilizer-applications/metrics.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-calculator/src/index.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.tsfdm-calculator/src/balance/nitrogen/types.d.ts
📚 Learning: 2025-11-24T10:43:09.278Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 343
File: fdm-app/app/components/blocks/balance/organic-matter-chart.tsx:19-25
Timestamp: 2025-11-24T10:43:09.278Z
Learning: In the organic matter balance chart (fdm-app), degradation values are multiplied by -1 before plotting to show supply and degradation bars side-by-side with positive magnitudes, enabling direct visual comparison. This is a deliberate design choice for the organic matter chart visualization, different from the nitrogen balance chart which uses separate stacks.
Applied to files:
fdm-calculator/src/balance/nitrogen/supply/deposition.test.tsfdm-calculator/src/balance/organic-matter/index.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-calculator/src/index.tsfdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2025-05-27T19:56:48.556Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 143
File: fdm-app/app/components/custom/balance/nitrogen-chart.tsx:73-85
Timestamp: 2025-05-27T19:56:48.556Z
Learning: In nitrogen balance charts, supply should be in a separate stack from removal and emission. Supply represents nitrogen inputs while removal and emission represent different types of nitrogen outputs, so they should be visually grouped differently using different stackId values.
Applied to files:
fdm-calculator/src/balance/nitrogen/supply/deposition.test.tsfdm-app/app/components/blocks/fertilizer-applications/metrics.tsx
📚 Learning: 2025-02-13T08:35:59.306Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.$b_id_farm.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx:114-124
Timestamp: 2025-02-13T08:35:59.306Z
Learning: The HarvestForm component in fdm-app expects undefined (not 0) for b_lu_yield when no yield information is available, as 0 would incorrectly imply that yield data exists.
Applied to files:
fdm-calculator/src/balance/nitrogen/emission/ammonia/residues.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-calculator/src/balance/nitrogen/removal/residue.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-calculator/src/balance/nitrogen/removal/harvest.test.tsfdm-calculator/src/balance/nitrogen/index.test.tsfdm-calculator/src/balance/nitrogen/removal/index.test.ts
📚 Learning: 2025-08-11T12:24:32.200Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx:53-53
Timestamp: 2025-08-11T12:24:32.200Z
Learning: In `fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx`, the NMI API for cultivations guarantees that each year will be unique in the cultivation history data, so using `cultivation.year` as a React list key is safe and won't cause duplicate key warnings.
Applied to files:
fdm-calculator/src/balance/nitrogen/emission/ammonia/residues.test.tsfdm-calculator/src/balance/nitrogen/removal/residue.test.tsfdm-calculator/src/balance/nitrogen/index.test.tsfdm-calculator/src/balance/nitrogen/removal/index.test.ts
📚 Learning: 2025-09-23T12:29:34.184Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A shared layout component `FarmLayoutBase` has been created in `components/custom/farm-layout-base.tsx` to maintain consistency across farm-related pages. The component handles farm selection dropdown, breadcrumb navigation, and provides a common layout structure.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsx
📚 Learning: 2025-07-21T12:06:07.070Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 156
File: fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts:295-303
Timestamp: 2025-07-21T12:06:07.070Z
Learning: Functions in the fdm-calculator with "NL2025" in their names are specifically designed for Netherlands 2025 agricultural norms calculation and hardcoded 2025 dates are appropriate in this context, as different years would have separate calculation modules.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-calculator/src/index.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: When using `updateField` from fdm-core, all 8 parameters must be provided in order: fdm, b_id, b_name, b_geometry, b_area, b_id_source, b_id_farm, and b_id_farm_source.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-02-13T09:03:11.890Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.create.$b_id_farm.cultivations.$b_lu_catalogue.crop.harvest._index.tsx:111-135
Timestamp: 2025-02-13T09:03:11.890Z
Learning: When adding multiple harvests in fdm-app, use Promise.all instead of Promise.allSettled to ensure atomic behavior - if one harvest addition fails, all should fail and rollback to maintain data consistency.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-calculator/src/balance/nitrogen/input.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-calculator/src/balance/nitrogen/removal/harvest.test.ts
📚 Learning: 2025-01-23T15:17:23.028Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.028Z
Learning: The `addField` function in fdm-core should use database transactions and field verification to ensure field availability before resolving its promise, eliminating the need for sleep workarounds.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-calculator/src/balance/nitrogen/input.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsx
📚 Learning: 2025-01-31T15:34:20.850Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:34:20.850Z
Learning: The `updateField` function in fdm-core has optional parameters that don't need to be passed as undefined. Only `fdm` and `b_id` are required.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core requires 8 parameters: fdm, b_id (required), and 6 optional parameters (b_name, b_id_source, b_geometry, b_acquiring_date, b_acquiring_method, b_discarding_date).
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-05-26T10:32:15.538Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:236-238
Timestamp: 2025-05-26T10:32:15.538Z
Learning: In nitrogen balance calculations for agricultural systems, removal and volatilization are calculated as negative values by definition since they represent nitrogen losses from the system. The balance calculation uses addition (.add()) for all components because removal and volatilization are already negative, so adding them effectively subtracts the losses from the supply.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-app/app/components/blocks/fertilizer-applications/metrics.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsxfdm-calculator/src/balance/nitrogen/removal/index.test.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core has optional parameters after `fdm` and `b_id`. The TypeScript definitions might show 8 required parameters due to a potential version mismatch.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsxfdm-calculator/src/balance/organic-matter/types.tsfdm-calculator/src/balance/nitrogen/input.tsfdm-calculator/src/balance/nitrogen/supply/deposition.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsxfdm-calculator/src/index.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/types.d.ts
📚 Learning: 2024-11-25T12:42:32.783Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 6
File: fdm-app/vite.config.ts:5-9
Timestamp: 2024-11-25T12:42:32.783Z
Learning: In the `fdm-app` project, SvenVw is preparing for migration to Remix v3 and may include type declarations or configurations for v3 features in advance, such as in `vite.config.ts`.
Applied to files:
fdm-app/app/components/blocks/balance/nitrogen-chart.tsx
📚 Learning: 2025-01-23T15:17:23.027Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.027Z
Learning: The `addField` function in fdm-core should verify field creation within the same transaction by checking the existence of the field and all its required relations (field data, acquiring info, geometry) before resolving its promise.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsx
📚 Learning: 2025-09-23T12:27:07.391Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.
Applied to files:
fdm-calculator/src/balance/nitrogen/input.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsx
📚 Learning: 2025-03-04T09:03:04.358Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 87
File: fdm-core/src/farm.ts:22-25
Timestamp: 2025-03-04T09:03:04.358Z
Learning: In the FDM codebase, functions that insert/create data (like `addFarm`) intentionally use `principal_id: string` instead of `PrincipalId` to enforce that only a single principal ID can be used for creation operations, while read operations use the more flexible `PrincipalId` type which supports both single IDs and arrays of IDs.
Applied to files:
fdm-app/app/integrations/calculator.ts
🧬 Code graph analysis (11)
fdm-calculator/src/balance/nitrogen/supply/deposition.test.ts (5)
fdm-calculator/src/index.ts (1)
NitrogenBalanceInput(14-14)fdm-calculator/src/balance/nitrogen/types.d.ts (1)
NitrogenBalanceInput(532-540)fdm-calculator/src/balance/nitrogen/supply/deposition.ts (1)
calculateAllFieldsNitrogenSupplyByDeposition(19-80)fdm-core/src/index.ts (1)
Field(112-112)fdm-core/src/db/schema.ts (1)
fields(92-108)
fdm-calculator/src/balance/organic-matter/index.test.ts (2)
fdm-calculator/src/balance/organic-matter/index.ts (1)
calculateOrganicMatterBalanceField(96-151)fdm-calculator/src/balance/organic-matter/types.ts (2)
OrganicMatterBalanceFieldResultNumeric(330-335)OrganicMatterBalanceFieldNumeric(322-327)
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsx (1)
fdm-app/app/integrations/calculator.ts (1)
getOrganicMatterBalanceForFarm(121-140)
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx (2)
fdm-calculator/src/index.ts (1)
calculateNitrogenBalance(4-4)fdm-calculator/src/balance/nitrogen/index.ts (1)
calculateNitrogenBalance(32-75)
fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx (2)
fdm-calculator/src/index.ts (1)
NitrogenBalanceFieldResultNumeric(13-13)fdm-calculator/src/balance/nitrogen/types.d.ts (1)
NitrogenBalanceFieldResultNumeric(670-675)
fdm-calculator/src/balance/organic-matter/types.ts (2)
fdm-calculator/src/index.ts (1)
FieldInput(10-10)fdm-calculator/src/balance/nitrogen/types.d.ts (3)
FieldInput(471-498)FertilizerDetail(517-527)CultivationDetail(503-512)
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx (1)
fdm-app/app/integrations/calculator.ts (1)
getNitrogenBalanceForFarm(61-80)
fdm-calculator/src/balance/nitrogen/input.ts (2)
fdm-calculator/src/shared/public-data-url.ts (1)
getFdmPublicDataUrl(1-3)fdm-calculator/src/balance/nitrogen/supply/deposition.ts (1)
calculateAllFieldsNitrogenSupplyByDeposition(19-80)
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter.$b_id.tsx (1)
fdm-app/app/integrations/calculator.ts (1)
getOrganicMatterBalanceForField(83-119)
fdm-calculator/src/balance/nitrogen/index.test.ts (3)
fdm-core/src/index.ts (1)
FdmType(79-79)fdm-calculator/src/index.ts (1)
calculateNitrogenBalance(4-4)fdm-calculator/src/balance/nitrogen/index.ts (1)
calculateNitrogenBalance(32-75)
fdm-calculator/src/balance/nitrogen/types.d.ts (2)
fdm-calculator/src/index.ts (2)
NitrogenBalanceFieldInput(11-11)FieldInput(10-10)fdm-calculator/src/balance/organic-matter/types.ts (3)
FieldInput(194-218)FertilizerDetail(234-238)CultivationDetail(223-229)
🪛 Biome (2.1.2)
fdm-calculator/src/balance/nitrogen/index.test.ts
[error] 13-13: Do not add then to an object.
(lint/suspicious/noThenProperty)
🪛 GitHub Actions: Type Check
fdm-calculator/src/balance/nitrogen/removal/harvest.test.ts
[error] 9-13: Type '{ b_lu: string; b_lu_catalogue: string; m_cropresidue: true; b_lu_start: null; b_lu_end: null; }' is missing the following properties from type 'Pick<Cultivation, "b_lu_catalogue" | "b_lu_name" | "b_lu_croprotation" | "b_lu" | "b_lu_start" | "b_lu_end" | "m_cropresidue">': b_lu_name, b_lu_croprotation. (TS2739). Command: tsc --noEmit
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
fdm-app/app/integrations/calculator.ts (1)
29-63: RenamegetNitrogenBalanceforFieldtogetNitrogenBalanceForFieldfor naming consistency.All related balance functions use uppercase
For(getNitrogenBalanceForFarm,getOrganicMatterBalanceForField,getOrganicMatterBalanceForFarm), but this function uses lowercasefor, creating an inconsistent naming pattern.Requires updates in:
fdm-app/app/integrations/calculator.ts(definition at line 29)fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx(import and usage)
🤖 Fix all issues with AI agents
In @.changeset/frank-boxes-join.md:
- Line 5: Fix the typo in the changeset description: change "senstive" to
"sensitive" in the sentence referencing the withCalculationCache option so it
reads "For withCalculationCache add the option to provide which sensitive keys
should be redacted in the cache"; ensure the word "sensitive" is spelled
correctly wherever this exact sentence or similar wording appears.
🧹 Nitpick comments (8)
fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx (1)
329-339: Consider displaying the error message when available.The
NitrogenBalanceFieldResultNumerictype includes anerrorMessagefield that could provide context when the balance is unavailable. Currently, only a generic message is shown.💡 Suggested improvement
{(resolvedNitrogenBalance) => { const balance = resolvedNitrogenBalance?.balance + const errorMessage = + resolvedNitrogenBalance?.errorMessage if (!balance) { return ( <div> - Geen balans - beschikbaar + {errorMessage ?? + "Geen balans beschikbaar"} </div> ) }fdm-calculator/src/balance/nitrogen/target.test.ts (1)
16-27: Test helper updated correctly to support new cultivation fields.The function signature and implementation properly reflect the new
b_lu_croprotationandb_lu_namefields from the updatedFieldInputtype.Consider expanding test coverage to include the unused crop rotation types ("maize" and "other") to ensure the calculation handles all four enumerated values correctly. Additionally, the hardcoded
b_lu_name: "Test Cultivation"could optionally be made more descriptive per test case (e.g., "Test Grassland", "Test Cereal Crop") for better test readability.fdm-calculator/src/balance/organic-matter/performance.test.ts (1)
12-22: Consider using manual mocks instead of vi utilities.Based on learnings, the project has experienced issues with Vitest's
vimocking utilities and prefers manual JavaScript mocks using plain objects.♻️ Alternative manual mock approach
-// Mock FdmType -const mockFdm = { - select: vi.fn().mockReturnThis(), - from: vi.fn().mockReturnThis(), - where: vi.fn().mockReturnThis(), - limit: vi.fn().mockReturnThis(), - then: vi.fn((resolve) => - resolve ? Promise.resolve(resolve([])) : Promise.resolve([]), - ), // Simulate cache miss - insert: vi.fn().mockReturnThis(), - values: vi.fn().mockResolvedValue(undefined), -} as unknown as FdmType +// Mock FdmType +const mockFdm = { + select: () => mockFdm, + from: () => mockFdm, + where: () => mockFdm, + limit: () => mockFdm, + then: (resolve?: (value: unknown) => unknown) => + resolve ? Promise.resolve(resolve([])) : Promise.resolve([]), + insert: () => mockFdm, + values: () => Promise.resolve(undefined), +} as unknown as FdmTypeThis avoids vi.fn() while maintaining the chainable mock behavior needed for the test. The static analysis warning about the
thenproperty would also be resolved since it's now a plain method rather than a vi.fn() mock.Based on learnings, as the project has had past issues with vi mocking utilities.
fdm-calculator/src/balance/nitrogen/performance.test.ts (1)
13-21: Consider using manual mocks instead of vi utilities.Based on learnings, the project has experienced issues with Vitest's
vimocking utilities and prefers manual JavaScript mocks using plain objects.♻️ Alternative manual mock approach
-// Mock FdmType -const mockFdm = { - select: vi.fn().mockReturnThis(), - from: vi.fn().mockReturnThis(), - where: vi.fn().mockReturnThis(), - limit: vi.fn().mockReturnThis(), - then: vi.fn((resolve) => resolve ? Promise.resolve(resolve([])) : Promise.resolve([])), // Simulate cache miss - insert: vi.fn().mockReturnThis(), - values: vi.fn().mockResolvedValue(undefined), -} as unknown as FdmType +// Mock FdmType +const mockFdm = { + select: () => mockFdm, + from: () => mockFdm, + where: () => mockFdm, + limit: () => mockFdm, + then: (resolve?: (value: unknown) => unknown) => + resolve ? Promise.resolve(resolve([])) : Promise.resolve([]), + insert: () => mockFdm, + values: () => Promise.resolve(undefined), +} as unknown as FdmTypeThis avoids vi.fn() while maintaining the chainable mock behavior needed for the test. The static analysis warning about the
thenproperty would also be resolved since it's now a plain method rather than a vi.fn() mock.Based on learnings, as the project has had past issues with vi mocking utilities.
fdm-core/src/calculator.test.ts (1)
279-321: Good test coverage for the redaction feature.The test verifies:
- The original calculation function receives the full unredacted input
- The cache hash is computed from the redacted input
- The stored input in the database has sensitive values redacted
Consider adding a test case for nested sensitive keys (e.g.,
{ data: { apiKey: "secret" } }withsensitiveKeys: ["apiKey"]) to ensure the recursive redaction works as expected.📝 Optional: Add test for nested sensitive keys
it("should redact nested sensitive keys from cache key and storage", async () => { const calculate = vi.fn(async (inputs: { data: { apiKey: string; value: string } }) => { return `result for ${inputs.data.value}` }) const calculatorVersion = "1.0.0" const input = { data: { apiKey: "nested-secret", value: "public" } } const getCalculation = withCalculationCache( calculate, "calculateWithNestedSecrets", calculatorVersion, ["apiKey"] ) await expect(getCalculation(fdm, input)).resolves.toBe("result for public") // Verify original function received full input expect(calculate).toHaveBeenCalledWith(input) // Expected input for cache (with nested REDACTED secret) const expectedCacheInput = { data: { apiKey: "REDACTED", value: "public" } } const expectedHash = generateCalculationHash( "calculateWithNestedSecrets", calculatorVersion, expectedCacheInput ) const cached = await fdm .select() .from(calculationCache) .where(eq(calculationCache.calculation_hash, expectedHash)) expect(cached).toHaveLength(1) const storedInput = cached[0].input as any expect(storedInput.data.apiKey).toBe("REDACTED") })fdm-calculator/src/balance/organic-matter/index.test.ts (1)
95-100: Verify the comment calculation for weighted averages.The comment states "Total Degradation = (20010 + 3005) / (10+5) = 3500 / 15 = 233.33 -> 233", but the degradation values in the test data are
-200and-300(negative by design per learnings). The expected result-233is correct, but the comment omits the negative signs which could be confusing.📝 Suggested comment clarification
- // Total Supply = (500*10 + 400*5) / (10+5) = 7000 / 15 = 466.67 -> 467 - // Total Degradation = (200*10 + 300*5) / (10+5) = 3500 / 15 = 233.33 -> 233 - // Total Balance = 466.67 - 233.33 = 233.34 -> 233 + // Total Supply = (500*10 + 400*5) / (10+5) = 7000 / 15 = 466.67 -> 467 + // Total Degradation = ((-200)*10 + (-300)*5) / (10+5) = -3500 / 15 = -233.33 -> -233 + // Total Balance = 466.67 + (-233.33) = 233.34 -> 233 (supply + degradation, since degradation is negative)fdm-calculator/src/balance/nitrogen/index.ts (1)
62-62: Consider preserving full error context.Stripping
"Error: "prefix viaString(error).replace("Error: ", "")may inadvertently remove legitimate occurrences of "Error: " within the error message itself.🔧 Safer error message extraction
- errorMessage: String(error).replace("Error: ", ""), + errorMessage: error instanceof Error ? error.message : String(error),fdm-calculator/src/balance/organic-matter/index.ts (1)
68-68: Same error message stripping concern as nitrogen balance.This uses the same pattern
String(error).replace("Error: ", "")which may inadvertently affect error messages containing "Error: " as part of the content.🔧 Safer error message extraction
- errorMessage: String(error).replace("Error: ", ""), + errorMessage: error instanceof Error ? error.message : String(error),
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
.changeset/frank-boxes-join.md.changeset/old-papayas-retire.md.changeset/young-dolls-cheat.mdfdm-app/app/components/blocks/fertilizer-applications/metrics.tsxfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.tsfdm-calculator/src/balance/nitrogen/performance.test.tsfdm-calculator/src/balance/nitrogen/removal/harvest.test.tsfdm-calculator/src/balance/nitrogen/removal/residue.test.tsfdm-calculator/src/balance/nitrogen/target.test.tsfdm-calculator/src/balance/nitrogen/types.d.tsfdm-calculator/src/balance/organic-matter/index.test.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-calculator/src/balance/organic-matter/performance.test.tsfdm-calculator/src/nutrient-advice/index.tsfdm-core/src/calculator.test.tsfdm-core/src/calculator.ts
✅ Files skipped from review due to trivial changes (1)
- .changeset/young-dolls-cheat.md
🚧 Files skipped from review as they are similar to previous changes (2)
- fdm-calculator/src/balance/nitrogen/removal/harvest.test.ts
- fdm-calculator/src/balance/nitrogen/removal/residue.test.ts
🧰 Additional context used
🧠 Learnings (36)
📓 Common learnings
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.711Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 343
File: fdm-calculator/src/balance/organic-matter/types.d.ts:12-132
Timestamp: 2025-11-21T10:02:25.556Z
Learning: In the organic matter balance calculation system (fdm-calculator), degradation values are negative by definition. This means the balance calculation using supply.total.plus(degradation.total) is correct, as it effectively computes supply - |degradation|. This follows the same pattern as the nitrogen balance system.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 343
File: fdm-app/app/components/blocks/balance/organic-matter-chart.tsx:19-25
Timestamp: 2025-11-24T10:43:09.278Z
Learning: In the organic matter balance chart (fdm-app), degradation values are multiplied by -1 before plotting to show supply and degradation bars side-by-side with positive magnitudes, enabling direct visual comparison. This is a deliberate design choice for the organic matter chart visualization, different from the nitrogen balance chart which uses separate stacks.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 236
File: fdm-calculator/src/balance/nitrogen/index.ts:173-0
Timestamp: 2025-08-14T14:31:55.384Z
Learning: In nitrogen balance calculations for agricultural systems, the balance should only include ammonia emissions (emission.ammonia.total) and should not include nitrate leaching from the emission calculation. The nitrate component (emission.nitrate) should be excluded from the balance formula.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 279
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx:277-283
Timestamp: 2025-09-26T08:34:50.413Z
Learning: In the fdm project, fdm-core and fdm-app are updated together as part of a monorepo structure, which eliminates legacy data concerns when new fields like b_isproductive are introduced. Both packages are synchronized, so there's no need for defensive coding against undefined values for newly introduced database fields.
📚 Learning: 2025-08-13T10:33:05.313Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.
Applied to files:
.changeset/old-papayas-retire.mdfdm-core/src/calculator.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.tsfdm-calculator/src/balance/organic-matter/index.ts.changeset/frank-boxes-join.md
📚 Learning: 2025-07-21T12:06:07.070Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 156
File: fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts:295-303
Timestamp: 2025-07-21T12:06:07.070Z
Learning: Functions in the fdm-calculator with "NL2025" in their names are specifically designed for Netherlands 2025 agricultural norms calculation and hardcoded 2025 dates are appropriate in this context, as different years would have separate calculation modules.
Applied to files:
.changeset/old-papayas-retire.mdfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2025-11-21T10:02:25.556Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 343
File: fdm-calculator/src/balance/organic-matter/types.d.ts:12-132
Timestamp: 2025-11-21T10:02:25.556Z
Learning: In the organic matter balance calculation system (fdm-calculator), degradation values are negative by definition. This means the balance calculation using supply.total.plus(degradation.total) is correct, as it effectively computes supply - |degradation|. This follows the same pattern as the nitrogen balance system.
Applied to files:
.changeset/old-papayas-retire.mdfdm-calculator/src/balance/nitrogen/types.d.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2025-09-23T12:29:34.184Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.
Applied to files:
.changeset/old-papayas-retire.md
📚 Learning: 2025-02-14T09:56:37.606Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 75
File: fdm-app/app/routes/farm.$b_id_farm.field.$b_id.fertilizer.tsx:68-71
Timestamp: 2025-02-14T09:56:37.606Z
Learning: The `calculateDose` function in `svenvw/fdm-calculator` is a synchronous function that includes built-in validation for negative application amounts and nutrient rates.
Applied to files:
.changeset/old-papayas-retire.mdfdm-core/src/calculator.tsfdm-app/app/components/blocks/fertilizer-applications/metrics.tsxfdm-calculator/src/balance/nitrogen/performance.test.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.tsfdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2025-09-23T12:37:58.711Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.711Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.
Applied to files:
.changeset/old-papayas-retire.mdfdm-calculator/src/balance/organic-matter/performance.test.tsfdm-calculator/src/balance/nitrogen/performance.test.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.tsfdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2025-09-26T08:34:50.413Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 279
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx:277-283
Timestamp: 2025-09-26T08:34:50.413Z
Learning: In the fdm project, fdm-core and fdm-app are updated together as part of a monorepo structure, which eliminates legacy data concerns when new fields like b_isproductive are introduced. Both packages are synchronized, so there's no need for defensive coding against undefined values for newly introduced database fields.
Applied to files:
.changeset/old-papayas-retire.mdfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2025-05-26T10:32:00.674Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:162-168
Timestamp: 2025-05-26T10:32:00.674Z
Learning: In the nitrogen balance calculation system (fdm-calculator), removal and volatilization values are negative by definition. This means the balance calculation using supply.total.add(removal.total).add(volatilization.total) is correct, as it effectively computes supply - |removal| - |volatilization|.
Applied to files:
.changeset/old-papayas-retire.mdfdm-app/app/components/blocks/fertilizer-applications/metrics.tsxfdm-calculator/src/balance/nitrogen/types.d.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-09-23T12:27:07.391Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.
Applied to files:
.changeset/old-papayas-retire.md
📚 Learning: 2025-09-23T10:02:32.123Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.$p_id.tsx:151-164
Timestamp: 2025-09-23T10:02:32.123Z
Learning: The getFertilizer function from svenvw/fdm-core throws an exception if the fertilizer doesn't exist, rather than returning null or undefined.
Applied to files:
.changeset/old-papayas-retire.mdfdm-calculator/src/balance/nitrogen/performance.test.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.tsfdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2024-11-27T12:15:36.425Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-data/src/cultivations/index.test.ts:57-59
Timestamp: 2024-11-27T12:15:36.425Z
Learning: In `fdm-data/src/cultivations/index.test.ts`, the `fdm` object created by `drizzle` does not have an `.end()` method. Cleanup code should not attempt to call `fdm.end();`.
Applied to files:
fdm-calculator/src/balance/organic-matter/performance.test.tsfdm-calculator/src/balance/organic-matter/index.test.tsfdm-calculator/src/balance/nitrogen/performance.test.ts
📚 Learning: 2025-03-06T15:23:48.352Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 95
File: fdm-core/src/catalogues.ts:134-170
Timestamp: 2025-03-06T15:23:48.352Z
Learning: When writing tests for fdm-core, avoid using Vitest's `vi` mocking utilities and prefer manual JavaScript mocks.
Applied to files:
fdm-calculator/src/balance/organic-matter/performance.test.tsfdm-calculator/src/balance/nitrogen/performance.test.ts
📚 Learning: 2024-11-27T11:27:27.797Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-data/src/cultivations/index.test.ts:57-59
Timestamp: 2024-11-27T11:27:27.797Z
Learning: When cleaning up test data in `afterAll` hooks in `fdm-data/src/cultivations/index.test.ts`, use `fdm.delete(schema.tableName).execute();` to delete rows from a table without dropping the table itself.
Applied to files:
fdm-calculator/src/balance/organic-matter/performance.test.tsfdm-calculator/src/balance/nitrogen/performance.test.ts
📚 Learning: 2025-03-06T14:58:48.603Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 95
File: fdm-core/src/cultivation.ts:67-73
Timestamp: 2025-03-06T14:58:48.603Z
Learning: When writing unit tests for the fdm project, avoid using Vitest's mocking utilities (vi) as it has caused problems in the past not related to the actual code. Instead, use simple object literals with methods that throw errors to test error handling.
Applied to files:
fdm-calculator/src/balance/organic-matter/performance.test.tsfdm-calculator/src/balance/nitrogen/performance.test.ts
📚 Learning: 2025-08-11T11:55:26.053Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/integrations/nmi.ts:54-0
Timestamp: 2025-08-11T11:55:26.053Z
Learning: The NMI API Estimates endpoint (`https://api.nmi-agro.nl/estimates`) always returns the fields `b_gwl_ghg`, `b_gwl_glg`, and `cultivations` according to its specification. These fields should be kept as required (not optional) in the TypeScript return type and Zod validation schema in `fdm-app/app/integrations/nmi.ts`.
Applied to files:
fdm-app/app/components/blocks/fertilizer-applications/metrics.tsxfdm-calculator/src/balance/nitrogen/target.test.tsfdm-calculator/src/balance/nitrogen/types.d.tsfdm-calculator/src/balance/organic-matter/index.test.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.tsfdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2025-08-14T14:31:55.384Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 236
File: fdm-calculator/src/balance/nitrogen/index.ts:173-0
Timestamp: 2025-08-14T14:31:55.384Z
Learning: In nitrogen balance calculations for agricultural systems, the balance should only include ammonia emissions (emission.ammonia.total) and should not include nitrate leaching from the emission calculation. The nitrate component (emission.nitrate) should be excluded from the balance formula.
Applied to files:
fdm-app/app/components/blocks/fertilizer-applications/metrics.tsxfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-05-26T10:32:15.538Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:236-238
Timestamp: 2025-05-26T10:32:15.538Z
Learning: In nitrogen balance calculations for agricultural systems, removal and volatilization are calculated as negative values by definition since they represent nitrogen losses from the system. The balance calculation uses addition (.add()) for all components because removal and volatilization are already negative, so adding them effectively subtracts the losses from the supply.
Applied to files:
fdm-app/app/components/blocks/fertilizer-applications/metrics.tsxfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-05-27T19:56:48.556Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 143
File: fdm-app/app/components/custom/balance/nitrogen-chart.tsx:73-85
Timestamp: 2025-05-27T19:56:48.556Z
Learning: In nitrogen balance charts, supply should be in a separate stack from removal and emission. Supply represents nitrogen inputs while removal and emission represent different types of nitrogen outputs, so they should be visually grouped differently using different stackId values.
Applied to files:
fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx
📚 Learning: 2025-12-15T12:19:47.858Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:47.858Z
Learning: When reviewing code, prefer storing only non-sensitive UI/state data in sessionStorage. For map viewState (e.g., longitude/latitude), ensure it represents non-personal business data and that persistence across sessions is justified, documented, and respects user privacy. If persisting, use a clearly scoped, namespaced key, guard access with try/catch, and avoid syncing with servers or exposing data to third-party scripts. Apply this guideline to all TSX files that manage client-side UI state.
Applied to files:
fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx
📚 Learning: 2025-01-23T15:18:57.212Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-core/src/db/schema.ts:407-426
Timestamp: 2025-01-23T15:18:57.212Z
Learning: In the farm data model, each cultivation (b_lu) can have only one termination date but can have multiple harvest dates. This is enforced through the database schema where cultivationTerminating uses b_lu as primary key while cultivationHarvesting uses a composite primary key of b_id_harvestable and b_lu.
Applied to files:
fdm-calculator/src/balance/nitrogen/target.test.tsfdm-calculator/src/balance/nitrogen/types.d.tsfdm-calculator/src/balance/nitrogen/performance.test.tsfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core has optional parameters after `fdm` and `b_id`. The TypeScript definitions might show 8 required parameters due to a potential version mismatch.
Applied to files:
fdm-calculator/src/balance/nitrogen/types.d.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2025-03-06T14:38:52.315Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 95
File: fdm-core/src/cultivation.ts:67-73
Timestamp: 2025-03-06T14:38:52.315Z
Learning: When writing unit tests for the fdm project, avoid using vi.mock() as it has caused implementation problems in the past. Use direct mock functions with vi.fn() instead.
Applied to files:
fdm-calculator/src/balance/nitrogen/performance.test.ts
📚 Learning: 2025-03-06T15:23:29.958Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 95
File: fdm-core/src/catalogues.ts:96-132
Timestamp: 2025-03-06T15:23:29.958Z
Learning: When writing tests for the FDM codebase, avoid using Vitest's `vi` mocking utilities (`vi.spyOn()`, etc.). Prefer using natural errors, real function behavior, and actual inputs that trigger expected failures.
Applied to files:
fdm-calculator/src/balance/nitrogen/performance.test.ts
📚 Learning: 2025-03-06T15:20:37.653Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 95
File: fdm-core/src/catalogues.ts:12-50
Timestamp: 2025-03-06T15:20:37.653Z
Learning: Do not use vi (Vitest) mocking utilities in unit tests for the fdm project.
Applied to files:
fdm-calculator/src/balance/nitrogen/performance.test.ts
📚 Learning: 2025-01-23T15:17:23.028Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.028Z
Learning: The `addField` function in fdm-core should use database transactions and field verification to ensure field availability before resolving its promise, eliminating the need for sleep workarounds.
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-02-13T08:35:59.306Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.$b_id_farm.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx:114-124
Timestamp: 2025-02-13T08:35:59.306Z
Learning: The HarvestForm component in fdm-app expects undefined (not 0) for b_lu_yield when no yield information is available, as 0 would incorrectly imply that yield data exists.
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-01-23T15:17:23.027Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.027Z
Learning: The `addField` function in fdm-core should verify field creation within the same transaction by checking the existence of the field and all its required relations (field data, acquiring info, geometry) before resolving its promise.
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-01-31T15:34:20.850Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:34:20.850Z
Learning: The `updateField` function in fdm-core has optional parameters that don't need to be passed as undefined. Only `fdm` and `b_id` are required.
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-09-24T14:02:48.574Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.574Z
Learning: Both getFertilizer and getFertilizers functions in svenvw/fdm-core perform authorization checks using the user's principal_id to verify farm access before returning fertilizer data.
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-09-24T14:02:48.574Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.574Z
Learning: The getFertilizer function in svenvw/fdm-core does not perform authorization checks, unlike getFertilizers which includes a checkPermission call to verify farm access. This means getFertilizer(fdm, p_id) can potentially return fertilizer details for any fertilizer ID without validating user permissions.
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core requires 8 parameters: fdm, b_id (required), and 6 optional parameters (b_name, b_id_source, b_geometry, b_acquiring_date, b_acquiring_method, b_discarding_date).
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: When using `updateField` from fdm-core, all 8 parameters must be provided in order: fdm, b_id, b_name, b_geometry, b_area, b_id_source, b_id_farm, and b_id_farm_source.
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-03-04T09:03:04.358Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 87
File: fdm-core/src/farm.ts:22-25
Timestamp: 2025-03-04T09:03:04.358Z
Learning: In the FDM codebase, functions that insert/create data (like `addFarm`) intentionally use `principal_id: string` instead of `PrincipalId` to enforce that only a single principal ID can be used for creation operations, while read operations use the more flexible `PrincipalId` type which supports both single IDs and arrays of IDs.
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-11-24T10:43:09.278Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 343
File: fdm-app/app/components/blocks/balance/organic-matter-chart.tsx:19-25
Timestamp: 2025-11-24T10:43:09.278Z
Learning: In the organic matter balance chart (fdm-app), degradation values are multiplied by -1 before plotting to show supply and degradation bars side-by-side with positive magnitudes, enabling direct visual comparison. This is a deliberate design choice for the organic matter chart visualization, different from the nitrogen balance chart which uses separate stacks.
Applied to files:
fdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2025-04-04T14:27:39.518Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 116
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx:111-154
Timestamp: 2025-04-04T14:27:39.518Z
Learning: In the FDM application, cultivation retrieval logic should be centralized in utility functions rather than duplicated across loader and action functions to improve maintainability and ensure consistent behavior.
Applied to files:
fdm-calculator/src/balance/organic-matter/index.ts
🧬 Code graph analysis (6)
fdm-calculator/src/balance/organic-matter/performance.test.ts (2)
fdm-core/src/index.ts (1)
FdmType(79-79)fdm-core/src/db/schema.ts (2)
fields(92-108)cultivations(373-385)
fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx (2)
fdm-calculator/src/balance/nitrogen/types.d.ts (1)
NitrogenBalanceFieldResultNumeric(670-675)fdm-calculator/src/index.ts (1)
NitrogenBalanceFieldResultNumeric(13-13)
fdm-calculator/src/balance/nitrogen/target.test.ts (1)
fdm-calculator/src/balance/nitrogen/types.d.ts (1)
FieldInput(471-498)
fdm-core/src/calculator.test.ts (3)
fdm-core/src/calculator.ts (2)
withCalculationCache(163-314)generateCalculationHash(22-38)fdm-core/src/index.ts (1)
withCalculationCache(31-31)fdm-core/src/db/schema-calculator.ts (1)
calculationCache(6-13)
fdm-calculator/src/balance/organic-matter/index.test.ts (2)
fdm-calculator/src/balance/organic-matter/index.ts (2)
calculateOrganicMatterBalanceField(102-157)calculateOrganicMatterBalancesFieldToFarm(188-237)fdm-calculator/src/balance/organic-matter/types.ts (2)
OrganicMatterBalanceFieldResultNumeric(330-335)OrganicMatterBalanceFieldNumeric(322-327)
fdm-calculator/src/balance/nitrogen/performance.test.ts (3)
fdm-core/src/index.ts (1)
FdmType(79-79)fdm-calculator/src/balance/nitrogen/index.ts (1)
calculateNitrogenBalance(32-82)fdm-calculator/src/index.ts (1)
calculateNitrogenBalance(4-4)
🪛 Biome (2.1.2)
fdm-calculator/src/balance/organic-matter/performance.test.ts
[error] 17-17: Do not add then to an object.
(lint/suspicious/noThenProperty)
fdm-calculator/src/balance/nitrogen/performance.test.ts
[error] 18-18: Do not add then to an object.
(lint/suspicious/noThenProperty)
🪛 LanguageTool
.changeset/frank-boxes-join.md
[grammar] ~5-~5: Ensure spelling is correct
Context: ...onCache add the option to provide which senstive keys should be redacted in the cache
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🔇 Additional comments (29)
.changeset/old-papayas-retire.md (1)
2-6: Verify version bump type for breaking API changes.The changeset describes replacing
getNitrogenBalanceandgetOrganicMatterBalancewith new field-level variants. If these functions are part of the public API and are being removed (without backwards-compatible aliases), this constitutes a breaking change and should be bumped tomajorinstead ofminor.Please confirm:
- Are
getNitrogenBalanceandgetOrganicMatterBalancecurrently exported as part of the public API?- Are backwards-compatible aliases being maintained for these functions, or are they being completely removed?
- If they're being removed, the changeset should specify
"@svenvw/fdm-calculator": majorinstead ofminor.fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx (2)
1-7: LGTM on type updates.The import and type changes correctly align with the new
NitrogenBalanceFieldResultNumericwrapper type from the refactored field-level calculation API.Also applies to: 49-49
340-342: LGTM on balance calculation logic.The extraction of
balancewith the guard clause and thetaskcalculation (target - balance) correctly adapts to the new nested structure. The optional chaining onsupply,removal, andemissionprovides safe defaults while the direct access tobalance.balanceandbalance.targetassumes these are required fields onNitrogenBalanceFieldNumericonce the parent exists.Also applies to: 448-463
fdm-calculator/src/balance/nitrogen/target.test.ts (2)
42-59: LGTM!The helper function correctly propagates
b_lu_croprotationinto theCultivationDetailmap entries, maintaining consistency with the updated cultivation data model.
61-203: Test cases updated consistently.All eight test cases have been properly updated to pass the new
b_lu_croprotationparameter, with appropriate values ("grass" for grassland tests, "cereal" for arable land tests). The unchanged test assertions indicate that either the new fields don't yet affect the target calculation logic, or the chosen test values produce equivalent results to the previous implementation.fdm-calculator/src/nutrient-advice/index.ts (1)
108-108: LGTM! Proper security practice.Redacting the nmiApiKey from the cache is the correct approach to prevent sensitive API keys from being stored in the calculation cache.
fdm-calculator/src/balance/organic-matter/performance.test.ts (1)
130-159: LGTM! Well-structured performance test.The test properly:
- Generates realistic mock data for 300 fields
- Measures execution time using
process.hrtime.bigint- Validates both the result structure and performance constraint
- Sets an appropriate timeout (35s) slightly above the performance threshold (30s)
fdm-calculator/src/balance/nitrogen/performance.test.ts (2)
97-98: LGTM! Test data updated to match new field-level types.The additions of
b_lu_name,b_lu_croprotation, anddepositionSupplyproperly align with the updated type definitions for field-level balance calculations.Also applies to: 165-167
200-200: LGTM! Updated to match refactored API.The test correctly:
- Passes
mockFdmas the first parameter to align with the new function signature- Validates numeric return types for balance, supply, emission, and removal totals
These changes properly reflect the field-level refactor.
Also applies to: 211-214
fdm-calculator/src/balance/nitrogen/types.d.ts (3)
95-104: LGTM! Good type extraction.Extracting
NitrogenSupplyDepositionas a named type improves:
- Type reusability across the codebase
- Consistency with other supply types
- Documentation clarity
Also applies to: 141-141
473-498: LGTM! FieldInput properly extended for field-level calculations.The additions of:
b_lu_nameandb_lu_croprotationto cultivation fieldsdepositionSupplyto field inputThese properly support the refactor to field-level balance calculations and align with the test data updates across the PR.
529-553: LGTM! Clear separation of farm-level and field-level inputs.The new
NitrogenBalanceFieldInputtype properly models single-field calculation inputs, and the updated comment onNitrogenBalanceInputclarifies it's for farm-wide calculations. This distinction is essential for the field-level refactor.fdm-core/src/calculator.ts (4)
182-209: Redaction logic is well-implemented.The recursive redaction correctly handles primitives, arrays, Dates, and nested objects. The implementation preserves the original input for the calculation while using the sanitized version for caching and error logging.
One minor observation: if
sensitiveKeyscould grow large, consider using aSetfor O(1) lookup instead ofArray.includes(). However, for typical use cases with a small number of sensitive keys, this is acceptable.
211-216: Consistent use of sanitized input for hash generation.The
inputForCacheis correctly used for hash generation, ensuring that cache keys are based on redacted input values.
258-265: Consistent use of sanitized input for cache storage.The
inputForCacheis correctly passed tosetCachedCalculation, ensuring sensitive data is not persisted in the cache.
291-299: Consistent use of sanitized input for error logging.The
inputForCacheis correctly passed tosetCalculationError, preventing sensitive data from being logged in error records.fdm-calculator/src/balance/organic-matter/index.test.ts (2)
48-63: Test correctly updated for new field-level API.The test properly uses the new single-argument structure for
calculateOrganicMatterBalanceFieldwithfieldInput,fertilizerDetails,cultivationDetails, andtimeFrameproperties. The numeric assertions align with the expected Decimal-to-number conversion.
124-127: Good use oftoBeCloseTofor floating-point comparisons.Using
toBeCloseToinstead of exact equality is appropriate when dealing with floating-point arithmetic after Decimal-to-number conversion.fdm-app/app/integrations/calculator.ts (2)
50-52: Good defensive check for missing field.The error is thrown early with a clear message when the field is not found, preventing undefined behavior downstream.
86-127: Well-structured field-level organic matter balance function.The function follows the same pattern as the nitrogen balance function, maintaining consistency. The return type includes both
fieldResultandfieldInput, which is useful for downstream consumers that may need the input data.fdm-calculator/src/balance/nitrogen/index.ts (5)
32-82: Well-structured farm-level orchestration.The
calculateNitrogenBalancefunction now properly orchestrates per-field calculations with batch processing (50 fields at a time) to manage concurrency. Error handling is graceful, allowing partial results when individual fields fail.
117-119: Good validation for required timeframe dates.Early validation ensures both start and end dates are provided, preventing downstream errors.
154-162: Excellent edge case handling for non-overlapping timeframes.The logic correctly computes the intersection of field dates with the input timeframe and handles the case where there's no overlap by normalizing to an empty interval. This prevents issues with invalid date ranges.
235-239: Per-field caching correctly implemented.The
getNitrogenBalanceFieldis properly wrapped withwithCalculationCache, enabling caching at the field level as intended by the PR objectives.
304-382: Re-wrapping numeric values in Decimal for aggregation is correct.Since
NitrogenBalanceFieldResultNumericcontains numeric (not Decimal) values after per-field conversion, wrapping them back inDecimalfor weighted average calculations ensures precision is maintained during aggregation. The final result is then converted back to numeric viaconvertDecimalToNumberRecursive.fdm-calculator/src/balance/organic-matter/index.ts (4)
34-89: Consistent structure with nitrogen balance implementation.The
calculateOrganicMatterBalancefunction follows the same pattern as the nitrogen balance function, ensuring consistency across the codebase. Batch processing with error handling allows partial success.
102-157: Well-structured field-level calculation.The function properly:
- Builds lookup maps for fertilizer and cultivation details
- Combines soil analyses with required fields
- Calculates supply and degradation
- Returns numeric balance via
convertDecimalToNumberRecursive
170-174: Per-field caching correctly implemented.Consistent with the nitrogen balance implementation,
getOrganicMatterBalanceFieldis wrapped withwithCalculationCachefor field-level caching.
188-237: Farm-level aggregation follows established pattern.The weighted average calculation correctly uses
Decimalfor precision during aggregation, then converts the final result to numeric. The structure mirrors the nitrogen balance aggregation.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
fdm-core/src/calculator.test.ts (1)
363-365: Consider adding assertion for preserved non-redacted field.The test verifies the redacted key but doesn't explicitly assert that the non-redacted
valuefield is preserved in the nested object. Adding this assertion would make the test more complete.💡 Suggested addition
const storedInput = cached[0].input as any expect(storedInput.data.apiKey).toBe("REDACTED") + expect(storedInput.data.value).toBe("public") })
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
.changeset/frank-boxes-join.mdfdm-app/app/integrations/calculator.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsxfdm-calculator/src/balance/nitrogen/index.tsfdm-calculator/src/balance/nitrogen/performance.test.tsfdm-calculator/src/balance/organic-matter/index.test.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-calculator/src/balance/organic-matter/performance.test.tsfdm-core/src/calculator.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- fdm-calculator/src/balance/nitrogen/performance.test.ts
- fdm-calculator/src/balance/organic-matter/performance.test.ts
- .changeset/frank-boxes-join.md
🧰 Additional context used
🧠 Learnings (30)
📓 Common learnings
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 279
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx:277-283
Timestamp: 2025-09-26T08:34:50.413Z
Learning: In the fdm project, fdm-core and fdm-app are updated together as part of a monorepo structure, which eliminates legacy data concerns when new fields like b_isproductive are introduced. Both packages are synchronized, so there's no need for defensive coding against undefined values for newly introduced database fields.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.028Z
Learning: The `addField` function in fdm-core should use database transactions and field verification to ensure field availability before resolving its promise, eliminating the need for sleep workarounds.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.711Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.
📚 Learning: 2025-08-11T11:55:26.053Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/integrations/nmi.ts:54-0
Timestamp: 2025-08-11T11:55:26.053Z
Learning: The NMI API Estimates endpoint (`https://api.nmi-agro.nl/estimates`) always returns the fields `b_gwl_ghg`, `b_gwl_glg`, and `cultivations` according to its specification. These fields should be kept as required (not optional) in the TypeScript return type and Zod validation schema in `fdm-app/app/integrations/nmi.ts`.
Applied to files:
fdm-calculator/src/balance/organic-matter/index.test.tsfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2024-11-27T12:15:36.425Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-data/src/cultivations/index.test.ts:57-59
Timestamp: 2024-11-27T12:15:36.425Z
Learning: In `fdm-data/src/cultivations/index.test.ts`, the `fdm` object created by `drizzle` does not have an `.end()` method. Cleanup code should not attempt to call `fdm.end();`.
Applied to files:
fdm-calculator/src/balance/organic-matter/index.test.ts
📚 Learning: 2025-03-06T14:58:48.603Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 95
File: fdm-core/src/cultivation.ts:67-73
Timestamp: 2025-03-06T14:58:48.603Z
Learning: When writing unit tests for the fdm project, avoid using Vitest's mocking utilities (vi) as it has caused problems in the past not related to the actual code. Instead, use simple object literals with methods that throw errors to test error handling.
Applied to files:
fdm-core/src/calculator.test.ts
📚 Learning: 2025-03-06T15:23:48.352Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 95
File: fdm-core/src/catalogues.ts:134-170
Timestamp: 2025-03-06T15:23:48.352Z
Learning: When writing tests for fdm-core, avoid using Vitest's `vi` mocking utilities and prefer manual JavaScript mocks.
Applied to files:
fdm-core/src/calculator.test.ts
📚 Learning: 2025-09-23T12:29:34.184Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsxfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core has optional parameters after `fdm` and `b_id`. The TypeScript definitions might show 8 required parameters due to a potential version mismatch.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsxfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-08-13T10:33:05.313Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsxfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-07-21T12:06:07.070Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 156
File: fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts:295-303
Timestamp: 2025-07-21T12:06:07.070Z
Learning: Functions in the fdm-calculator with "NL2025" in their names are specifically designed for Netherlands 2025 agricultural norms calculation and hardcoded 2025 dates are appropriate in this context, as different years would have separate calculation modules.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsxfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2025-02-14T09:56:37.606Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 75
File: fdm-app/app/routes/farm.$b_id_farm.field.$b_id.fertilizer.tsx:68-71
Timestamp: 2025-02-14T09:56:37.606Z
Learning: The `calculateDose` function in `svenvw/fdm-calculator` is a synchronous function that includes built-in validation for negative application amounts and nutrient rates.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsxfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-09-26T08:34:50.413Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 279
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx:277-283
Timestamp: 2025-09-26T08:34:50.413Z
Learning: In the fdm project, fdm-core and fdm-app are updated together as part of a monorepo structure, which eliminates legacy data concerns when new fields like b_isproductive are introduced. Both packages are synchronized, so there's no need for defensive coding against undefined values for newly introduced database fields.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsxfdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2025-01-31T15:34:20.850Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:34:20.850Z
Learning: The `updateField` function in fdm-core has optional parameters that don't need to be passed as undefined. Only `fdm` and `b_id` are required.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsxfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-09-24T14:02:48.574Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.574Z
Learning: Both getFertilizer and getFertilizers functions in svenvw/fdm-core perform authorization checks using the user's principal_id to verify farm access before returning fertilizer data.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsxfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: When using `updateField` from fdm-core, all 8 parameters must be provided in order: fdm, b_id, b_name, b_geometry, b_area, b_id_source, b_id_farm, and b_id_farm_source.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsxfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core requires 8 parameters: fdm, b_id (required), and 6 optional parameters (b_name, b_id_source, b_geometry, b_acquiring_date, b_acquiring_method, b_discarding_date).
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsxfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-03-04T09:03:04.358Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 87
File: fdm-core/src/farm.ts:22-25
Timestamp: 2025-03-04T09:03:04.358Z
Learning: In the FDM codebase, functions that insert/create data (like `addFarm`) intentionally use `principal_id: string` instead of `PrincipalId` to enforce that only a single principal ID can be used for creation operations, while read operations use the more flexible `PrincipalId` type which supports both single IDs and arrays of IDs.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsxfdm-app/app/integrations/calculator.ts
📚 Learning: 2025-12-15T12:19:47.858Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:47.858Z
Learning: When reviewing code, prefer storing only non-sensitive UI/state data in sessionStorage. For map viewState (e.g., longitude/latitude), ensure it represents non-personal business data and that persistence across sessions is justified, documented, and respects user privacy. If persisting, use a clearly scoped, namespaced key, guard access with try/catch, and avoid syncing with servers or exposing data to third-party scripts. Apply this guideline to all TSX files that manage client-side UI state.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx
📚 Learning: 2025-08-14T14:31:55.384Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 236
File: fdm-calculator/src/balance/nitrogen/index.ts:173-0
Timestamp: 2025-08-14T14:31:55.384Z
Learning: In nitrogen balance calculations for agricultural systems, the balance should only include ammonia emissions (emission.ammonia.total) and should not include nitrate leaching from the emission calculation. The nitrate component (emission.nitrate) should be excluded from the balance formula.
Applied to files:
fdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-05-26T10:32:00.674Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:162-168
Timestamp: 2025-05-26T10:32:00.674Z
Learning: In the nitrogen balance calculation system (fdm-calculator), removal and volatilization values are negative by definition. This means the balance calculation using supply.total.add(removal.total).add(volatilization.total) is correct, as it effectively computes supply - |removal| - |volatilization|.
Applied to files:
fdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-05-26T10:32:15.538Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:236-238
Timestamp: 2025-05-26T10:32:15.538Z
Learning: In nitrogen balance calculations for agricultural systems, removal and volatilization are calculated as negative values by definition since they represent nitrogen losses from the system. The balance calculation uses addition (.add()) for all components because removal and volatilization are already negative, so adding them effectively subtracts the losses from the supply.
Applied to files:
fdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-09-23T12:37:58.711Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.711Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.
Applied to files:
fdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-01-23T15:17:23.028Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.028Z
Learning: The `addField` function in fdm-core should use database transactions and field verification to ensure field availability before resolving its promise, eliminating the need for sleep workarounds.
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-02-13T08:35:59.306Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.$b_id_farm.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx:114-124
Timestamp: 2025-02-13T08:35:59.306Z
Learning: The HarvestForm component in fdm-app expects undefined (not 0) for b_lu_yield when no yield information is available, as 0 would incorrectly imply that yield data exists.
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-01-23T15:17:23.027Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.027Z
Learning: The `addField` function in fdm-core should verify field creation within the same transaction by checking the existence of the field and all its required relations (field data, acquiring info, geometry) before resolving its promise.
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-09-23T10:02:32.123Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.$p_id.tsx:151-164
Timestamp: 2025-09-23T10:02:32.123Z
Learning: The getFertilizer function from svenvw/fdm-core throws an exception if the fertilizer doesn't exist, rather than returning null or undefined.
Applied to files:
fdm-app/app/integrations/calculator.tsfdm-calculator/src/balance/organic-matter/index.tsfdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-09-24T14:02:48.574Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.574Z
Learning: The getFertilizer function in svenvw/fdm-core does not perform authorization checks, unlike getFertilizers which includes a checkPermission call to verify farm access. This means getFertilizer(fdm, p_id) can potentially return fertilizer details for any fertilizer ID without validating user permissions.
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-01-23T15:18:57.212Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-core/src/db/schema.ts:407-426
Timestamp: 2025-01-23T15:18:57.212Z
Learning: In the farm data model, each cultivation (b_lu) can have only one termination date but can have multiple harvest dates. This is enforced through the database schema where cultivationTerminating uses b_lu as primary key while cultivationHarvesting uses a composite primary key of b_id_harvestable and b_lu.
Applied to files:
fdm-app/app/integrations/calculator.ts
📚 Learning: 2025-11-24T10:43:09.278Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 343
File: fdm-app/app/components/blocks/balance/organic-matter-chart.tsx:19-25
Timestamp: 2025-11-24T10:43:09.278Z
Learning: In the organic matter balance chart (fdm-app), degradation values are multiplied by -1 before plotting to show supply and degradation bars side-by-side with positive magnitudes, enabling direct visual comparison. This is a deliberate design choice for the organic matter chart visualization, different from the nitrogen balance chart which uses separate stacks.
Applied to files:
fdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2025-11-21T10:02:25.556Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 343
File: fdm-calculator/src/balance/organic-matter/types.d.ts:12-132
Timestamp: 2025-11-21T10:02:25.556Z
Learning: In the organic matter balance calculation system (fdm-calculator), degradation values are negative by definition. This means the balance calculation using supply.total.plus(degradation.total) is correct, as it effectively computes supply - |degradation|. This follows the same pattern as the nitrogen balance system.
Applied to files:
fdm-calculator/src/balance/organic-matter/index.ts
📚 Learning: 2025-04-04T14:27:39.518Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 116
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx:111-154
Timestamp: 2025-04-04T14:27:39.518Z
Learning: In the FDM application, cultivation retrieval logic should be centralized in utility functions rather than duplicated across loader and action functions to improve maintainability and ensure consistent behavior.
Applied to files:
fdm-calculator/src/balance/organic-matter/index.ts
🧬 Code graph analysis (4)
fdm-calculator/src/balance/organic-matter/index.test.ts (3)
fdm-calculator/src/balance/organic-matter/index.ts (2)
calculateOrganicMatterBalanceField(105-160)calculateOrganicMatterBalancesFieldToFarm(191-240)fdm-calculator/src/index.ts (3)
calculateOrganicMatterBalanceField(30-30)OrganicMatterBalanceFieldResultNumeric(36-36)OrganicMatterBalanceFieldNumeric(35-35)fdm-calculator/src/balance/organic-matter/types.ts (2)
OrganicMatterBalanceFieldResultNumeric(330-335)OrganicMatterBalanceFieldNumeric(322-327)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx (1)
fdm-app/app/integrations/calculator.ts (1)
getNitrogenBalanceForField(29-63)
fdm-calculator/src/balance/organic-matter/index.ts (4)
fdm-calculator/src/balance/organic-matter/types.ts (8)
OrganicMatterBalanceInput(243-255)OrganicMatterBalanceNumeric(338-345)OrganicMatterBalanceFieldResultNumeric(330-335)OrganicMatterBalanceFieldInput(261-273)OrganicMatterBalanceFieldNumeric(322-327)FertilizerDetail(234-238)CultivationDetail(223-229)SoilAnalysisPicked(184-189)fdm-calculator/src/balance/shared/soil.ts (1)
combineSoilAnalyses(26-114)fdm-calculator/src/balance/organic-matter/degradation.ts (1)
calculateOrganicMatterDegradation(32-102)fdm-calculator/src/balance/shared/conversion.ts (1)
convertDecimalToNumberRecursive(9-35)
fdm-calculator/src/balance/nitrogen/index.ts (6)
fdm-calculator/src/balance/nitrogen/types.d.ts (7)
NitrogenBalanceInput(532-540)NitrogenBalanceNumeric(678-717)NitrogenBalanceFieldResultNumeric(670-675)NitrogenBalanceFieldInput(545-553)NitrogenBalanceFieldNumeric(658-665)FertilizerDetail(517-527)SoilAnalysisPicked(457-466)fdm-calculator/src/balance/nitrogen/supply/index.ts (1)
calculateNitrogenSupply(29-79)fdm-calculator/src/balance/nitrogen/emission/nitrate/index.ts (1)
calculateNitrogenEmissionViaNitrate(32-123)fdm-calculator/src/balance/nitrogen/target.ts (1)
calculateTargetForNitrogenBalance(23-158)fdm-calculator/src/balance/shared/conversion.ts (1)
convertDecimalToNumberRecursive(9-35)fdm-core/src/calculator.ts (1)
withCalculationCache(163-314)
🔇 Additional comments (24)
fdm-core/src/calculator.test.ts (1)
279-323: LGTM!Comprehensive test covering top-level redaction. Properly verifies that:
- The original function receives the full unredacted input
- The cache hash is generated using the redacted input
- The stored input in DB contains the redacted value
fdm-calculator/src/balance/organic-matter/index.ts (5)
1-17: LGTM! Import structure aligns well with the field-level refactor.The imports correctly bring in
FdmTypeandwithCalculationCachefrom@svenvw/fdm-core, along with the new numeric type variants (OrganicMatterBalanceFieldNumeric,OrganicMatterBalanceFieldResultNumeric). This supports the bottom-up calculation architecture.
34-92: Farm-level orchestrator correctly delegates to cached field-level functions.The implementation properly:
- Processes fields in batches of 50 to avoid overwhelming concurrent promises.
- Calls
getOrganicMatterBalanceField(cached) for each field.- Gracefully handles per-field errors while continuing with other fields.
- Aggregates results via
calculateOrganicMatterBalancesFieldToFarm.This aligns with the PR objective of enabling granular per-field caching and accurate error logging.
105-160: Field-level calculation logic is well-structured.The function correctly:
- Builds local maps for fertilizer and cultivation details.
- Combines soil analyses with estimation enabled.
- Computes supply and degradation components.
- Returns numeric results via
convertDecimalToNumberRecursive.The balance formula
supply.total.plus(degradation.total)is correct since degradation values are negative by definition (consistent with learned pattern from nitrogen balance).
162-177: Cached field-level function correctly wraps the calculation.The
withCalculationCachewrapper enables per-field caching with the calculator version as part of the cache key. This is the core enabler for the bottom-up architecture.
191-239: Farm-level aggregation handles weighted averages correctly.The implementation:
- Filters out fields with errors before aggregation.
- Calculates area-weighted totals for supply and degradation.
- Computes per-hectare averages for the farm.
- Converts final result to numeric format.
One minor observation: the
successfulFieldBalancestype assertion at lines 199-201 is a pragmatic approach to narrow the type after filtering.fdm-calculator/src/balance/organic-matter/index.test.ts (4)
1-20: LGTM! Test imports and mocks are properly configured.The imports correctly reference the new numeric type variants, and the module mocks are appropriately set up for isolated unit testing.
48-63: Test correctly validates the new single-argument API.The test now passes an object with
fieldInput,fertilizerDetails,cultivationDetails, andtimeFrameproperties, matching the updatedOrganicMatterBalanceFieldInputtype signature. The assertions verify numeric return values (300, 500, -200) which confirms proper Decimal-to-number conversion.
66-101: Weighted average test math is correct.The test verifies:
- Field 1: area=10, supply=500, degradation=-200
- Field 2: area=5, supply=400, degradation=-300
- Expected avg supply: (500×10 + 400×5) / 15 = 466.67 → 467
- Expected avg degradation: (-200×10 + -300×5) / 15 = -233.33 → -233
- Expected balance: 467 + (-233) = 233 (with rounding)
The comments at lines 95-97 document the calculation clearly.
103-127: Error handling test verifies partial aggregation behavior.The test correctly validates that when one field has an error, only the successful field contributes to the farm totals. The
hasErrorsandfieldErrorMessagesare properly propagated.fdm-calculator/src/balance/nitrogen/index.ts (6)
1-19: LGTM! Imports correctly support the field-level refactor.The imports align with the new architecture:
FdmTypeandwithCalculationCachefrom fdm-core, numeric type variants for field results, and the conversion utility.
32-85: Farm-level orchestrator mirrors organic matter pattern correctly.The batch processing (size 50), per-field error handling, and aggregation flow are consistent with the organic matter implementation. This maintains architectural consistency across balance calculations.
146-165: Field-local timeframe intersection logic handles edge cases well.The implementation correctly:
- Computes intersection of input timeframe with field's active period.
- Uses
Number.NEGATIVE_INFINITY/POSITIVE_INFINITYfor open-ended fields.- Clamps to empty interval when no overlap exists (lines 162-165).
This ensures calculations only consider the relevant period for each field.
194-205: Balance and emission calculations follow domain rules.The balance formula
supply.total.add(removal.total).add(emission.ammonia.total)is correct per the learned pattern: removal and ammonia emissions are negative by definition. Nitrate emission is calculated separately and added to the emission object but correctly excluded from the balance (lines 198-205). Based on learnings, this matches the domain requirement that nitrate leaching should not be included in the nitrogen balance formula.
227-242: Cached field-level function enables per-field caching.The
withCalculationCachewrapper correctly wraps the synchronouscalculateNitrogenBalanceFieldfunction, enabling granular caching per field as specified in the PR objectives.
258-491: Complex aggregation handles all nitrogen components correctly.The implementation properly aggregates:
- Supply components (deposition, fixation, mineralisation, fertilizers by type)
- Removal components (harvests, residues)
- Emission components (ammonia by source, nitrate)
- Target values
All components are area-weighted and converted to per-hectare averages. The final conversion to numeric format ensures consistent output types.
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx (2)
38-40: LGTM! Import correctly updated to use renamed function.The import
getNitrogenBalanceForFieldmatches the new export fromcalculator.ts. The casing change (fromgetNitrogenBalanceforFieldtogetNitrogenBalanceForField) follows proper camelCase convention.
189-195: Field-level nitrogen balance call correctly wired.The call to
getNitrogenBalanceForFieldwith the required parameters (fdm,principal_id,b_id_farm,b_id,timeframe) aligns with the new field-level API. This enables per-field caching for the fertilizer application metrics display.fdm-app/app/integrations/calculator.ts (6)
1-26: LGTM! Imports correctly support the new field/farm balance API.The imports bring in both the cached field-level functions (
getNitrogenBalanceField,getOrganicMatterBalanceField) and the farm-level calculation functions (calculateNitrogenBalance,calculateOrganicMatterBalance), along with the necessary type definitions.
28-63: Field-level nitrogen balance retrieval is well-structured.The function:
- Collects input for a specific field using
collectInputForNitrogenBalancewith theb_idparameter.- Validates that the field exists (throws descriptive error if not found).
- Calls the cached
getNitrogenBalanceFieldfunction.- Returns a properly structured
NitrogenBalanceFieldResultNumeric.The guard at lines 50-52 ensures clear error messaging when a field is not found.
65-84: Farm-level nitrogen balance retrieval delegates correctly.The function collects input without a specific
b_id, then delegates tocalculateNitrogenBalancewhich handles the field iteration and aggregation. This is a clean separation of concerns.
86-127: Organic matter field balance returns both result and input.The return type
Promise<{ fieldResult: OrganicMatterBalanceFieldResultNumeric; fieldInput: FieldInput }>differs from the nitrogen counterpart by also returningfieldInput. This appears intentional for downstream UI needs (e.g., displaying field details alongside balance).If consistency with
getNitrogenBalanceForFieldis desired, consider whether the nitrogen function should also returnfieldInput, or document why organic matter needs it.
129-148: Farm-level organic matter balance retrieval is consistent with nitrogen pattern.The implementation follows the same pattern as
getNitrogenBalanceForFarm: collect input, delegate to calculation function. This maintains API consistency.
159-200: Type annotations tightened appropriately.Using
Field["b_id"]andField["b_centroid"]instead of generic string types improves type safety and documentation. This is a good refinement.
BoraIneviNMI
left a comment
There was a problem hiding this comment.
I am late but I tested it and looked through the code and it looks good.
Summary by CodeRabbit
Bug Fixes
New Features
Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.
Closes #406