Add 2026 year support with norms calculations#365
Conversation
🦋 Changeset detectedLatest commit: 990846b The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 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 |
WalkthroughAdds 2026 support across fdm-app and fdm-calculator: UI routes and loaders accept 2026; farm creation provides yearSelection; calculator gains NL 2026 value and filling implementations, centralized NL types, renamed/parametrized 2025 helpers, extensive tests, and new changesets. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant UI as fdm-app (Loader / UI)
participant Core as fdm-core (data)
participant Calc as fdm-calculator (collectors & calculators)
participant Cache as withCalculationCache
UI->>Core: getField(fdm, principalId, fieldId)
Core-->>UI: field | null
alt field found
UI->>Core: getGrazingIntention(farmId, 2026)
Core-->>UI: grazingFlag
UI->>Core: isOrganicCertificationValid(farmId, 2026-05-15)
Core-->>UI: organicFlag
UI->>Core: getCultivations(fieldId, {start:2026-01-01,end:2026-12-31})
Core-->>UI: cultivations
UI->>Core: getFertilizerApplications(fieldId, {start,end})
Core-->>UI: applications
UI->>Core: getFertilizers(farmId)
Core-->>UI: fertilizers
UI->>Calc: collectNL2026InputForFertilizerApplicationFilling(payload)
Calc->>Cache: check cache (key = name + pkg.calculatorVersion)
alt cache hit
Cache-->>Calc: cached result
else cache miss
Calc-->>Calc: compute filling/value (uses table9/table11/norms data)
Calc-->>Cache: store result
end
Calc-->>UI: NL2026NormsFillingInput / NormFilling result
else field not found
Core-->>UI: null -> UI errors "Field not found"
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ 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 #365 +/- ##
===============================================
- Coverage 87.62% 87.23% -0.39%
===============================================
Files 79 91 +12
Lines 3959 4434 +475
Branches 1145 1325 +180
===============================================
+ Hits 3469 3868 +399
- Misses 490 566 +76
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: 12
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/routes/farm.create._index.tsx (1)
212-218: Default value for derogation start year may show incorrect selection after 2025.The
Selectcomponent usesdefaultValue={String(new Date().getFullYear())}but the available options only go up to 2025 (line 229-231). When the current year becomes 2026, this default value won't match any available option, potentially causing the dropdown to appear without a selection.Consider using the form's
derogation_start_yeardefault value (2025) to ensure consistency:<Select onValueChange={ field.onChange } - defaultValue={String( - new Date().getFullYear(), - )} + defaultValue={String(field.value)} >
🧹 Nitpick comments (6)
fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm-data.ts (1)
1-9: Consider simplifying the data structure.The
fosfaatNormsDatais currently an array with a single element. If only one norm table is needed for 2026, consider exporting a plain object instead:-export const fosfaatNormsData = [ - { - Arm: { grasland: 120, bouwland: 120 }, - Laag: { grasland: 105, bouwland: 80 }, - Neutraal: { grasland: 95, bouwland: 70 }, - Ruim: { grasland: 90, bouwland: 60 }, - Hoog: { grasland: 75, bouwland: 40 }, - }, -] +export const fosfaatNormsData = { + Arm: { grasland: 120, bouwland: 120 }, + Laag: { grasland: 105, bouwland: 80 }, + Neutraal: { grasland: 95, bouwland: 70 }, + Ruim: { grasland: 90, bouwland: 60 }, + Hoog: { grasland: 75, bouwland: 40 }, +}This would eliminate the need for array indexing (
fosfaatNormsData[0]) in the consuming code. However, if the array structure is intentional for future extensibility or consistency with the 2025 implementation, this suggestion can be disregarded.fdm-calculator/src/norms/nl/2026/value/input.ts (1)
58-58: Minor: Comment numbering skips step 4.The comment on line 58 says "5. Get the details of the soil analyses" but step 4 was skipped. This appears to be a copy-paste artifact from the 2025 version where step 4 was likely removed (along with
is_derogatie_bedrijf).- // 5. Get the details of the soil analyses + // 4. Get the details of the soil analysesfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts (1)
6-6: Minor: Extra leading space in describe block name.The describe block has a leading space:
" calculateNL2026StikstofGebruiksNorm".-describe(" calculateNL2026StikstofGebruiksNorm", () => { +describe("calculateNL2026StikstofGebruiksNorm", () => {fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts (1)
36-36: JSDoc references nonexistent input parameter.The JSDoc comment mentions
@param {NL2026NormsInput} inputbutcalculateNL2026DierlijkeMestGebruiksNormtakes no parameters (which is correct for 2026 since there's no derogation logic)./** * Memoized version of {@link calculateNL2026DierlijkeMestGebruiksNorm}. * * This function is wrapped with `withCalculationCache` to optimize performance by caching * results based on the input and the current calculator version. * - * @param {NL2026NormsInput} input - An object containing all necessary parameters for the calculation. * @returns {Promise<DierlijkeMestGebruiksnormResult>} An object of type `DierlijkeMestGebruiksnormResult` containing the determined * nitrogen usage standard (`normValue`) and a `normSource` string explaining the rule applied. */fdm-calculator/src/norms/nl/types.ts (1)
51-55: Documentation references specific year but type is shared across years.This type is used by both NL2025 and NL2026 modules. Consider making the comment year-agnostic.
/** - * The cultivation name according to RVO's "Tabel 2 Stikstof landbouwgrond 2025" + * The cultivation name according to RVO's "Tabel 2 Stikstof landbouwgrond" * that was used to determine the legal limit. */ normSource: stringfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts (1)
213-220: Redundant day check in January condition.The condition
appDay <= 31is always true for January since it cannot have more than 31 days. This doesn't affect correctness but could be simplified for clarity.!( (appMonth >= 8 && appMonth <= 11) || - (appMonth === 0 && appDay <= 31) + appMonth === 0 )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
fdm-app/app/lib/calendar.tsis excluded by!fdm-app/app/lib/**pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (46)
.changeset/famous-hats-lick.md(1 hunks).changeset/ready-geckos-notice.md(1 hunks).changeset/silly-signs-ring.md(1 hunks).changeset/small-icons-teach.md(1 hunks)fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.$b_id.tsx(1 hunks)fdm-app/app/routes/farm.$b_id_farm.$calendar.norms._index.tsx(2 hunks)fdm-app/app/routes/farm.create._index.tsx(4 hunks)fdm-calculator/src/index.ts(1 hunks)fdm-calculator/src/norms/farm.ts(1 hunks)fdm-calculator/src/norms/index.test.ts(4 hunks)fdm-calculator/src/norms/index.ts(3 hunks)fdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2025/filling/input.test.ts(4 hunks)fdm-calculator/src/norms/nl/2025/filling/input.ts(1 hunks)fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2025/filling/types.d.ts(0 hunks)fdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts(2 hunks)fdm-calculator/src/norms/nl/2025/value/hoofdteelt.test.ts(6 hunks)fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts(2 hunks)fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts(5 hunks)fdm-calculator/src/norms/nl/2025/value/types.d.ts(0 hunks)fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.ts(1 hunks)fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.test.ts(1 hunks)fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/filling/input.test.ts(1 hunks)fdm-calculator/src/norms/nl/2026/filling/input.ts(1 hunks)fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts(1 hunks)fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/filling/table-11-mestcodes.ts(1 hunks)fdm-calculator/src/norms/nl/2026/filling/table-9.ts(1 hunks)fdm-calculator/src/norms/nl/2026/filling/types.d.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.test.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm-data.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.test.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/input.test.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/input.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm-data.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/types.d.ts(1 hunks)fdm-calculator/src/norms/nl/types.ts(1 hunks)
💤 Files with no reviewable changes (2)
- fdm-calculator/src/norms/nl/2025/filling/types.d.ts
- fdm-calculator/src/norms/nl/2025/value/types.d.ts
🧰 Additional context used
🧠 Learnings (32)
📓 Common learnings
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: 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: 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-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/ready-geckos-notice.md
📚 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:
.changeset/ready-geckos-notice.mdfdm-calculator/src/norms/nl/2026/filling/types.d.ts.changeset/famous-hats-lick.md
📚 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/ready-geckos-notice.mdfdm-calculator/src/norms/farm.ts.changeset/famous-hats-lick.md
📚 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/norms/farm.tsfdm-calculator/src/norms/nl/2026/filling/input.tsfdm-calculator/src/norms/nl/2026/value/input.ts.changeset/silly-signs-ring.mdfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.tsfdm-calculator/src/index.tsfdm-calculator/src/norms/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-calculator/src/norms/farm.tsfdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm-data.tsfdm-calculator/src/norms/nl/2026/value/input.test.tsfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.tsfdm-calculator/src/norms/nl/2025/filling/input.tsfdm-calculator/src/norms/nl/2026/filling/input.tsfdm-calculator/src/norms/nl/2026/value/input.tsfdm-calculator/src/norms/nl/2026/filling/table-11-mestcodes.tsfdm-calculator/src/norms/nl/2026/value/types.d.tsfdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.tsfdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/input.test.tsfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm-data.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.norms._index.tsxfdm-calculator/src/norms/nl/types.ts.changeset/silly-signs-ring.mdfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.norms.$b_id.tsxfdm-calculator/src/norms/nl/2025/filling/input.test.tsfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.test.tsfdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.test.tsfdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.test.tsfdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/types.d.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/hoofdteelt.tsfdm-calculator/src/norms/nl/2026/filling/table-9.tsfdm-calculator/src/index.tsfdm-calculator/src/norms/nl/2025/value/hoofdteelt.test.tsfdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.tsfdm-calculator/src/norms/index.tsfdm-calculator/src/norms/index.test.tsfdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.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-calculator/src/norms/farm.tsfdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm-data.tsfdm-calculator/src/norms/nl/2026/value/input.test.tsfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.tsfdm-calculator/src/norms/nl/2025/filling/input.tsfdm-calculator/src/norms/nl/2026/filling/input.tsfdm-calculator/src/norms/nl/2026/value/input.tsfdm-calculator/src/norms/nl/2026/filling/table-11-mestcodes.tsfdm-calculator/src/norms/nl/2026/value/types.d.tsfdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/input.test.tsfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm-data.tsfdm-calculator/src/norms/nl/types.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.tsfdm-app/app/routes/farm.$b_id_farm.$calendar.norms.$b_id.tsxfdm-calculator/src/norms/nl/2025/filling/input.test.tsfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.test.tsfdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.test.tsfdm-calculator/src/norms/nl/2026/filling/types.d.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/table-9.tsfdm-calculator/src/index.tsfdm-calculator/src/norms/nl/2025/value/hoofdteelt.test.tsfdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.tsfdm-calculator/src/norms/index.tsfdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.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/norms/nl/2026/value/input.test.tsfdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.tsfdm-calculator/src/norms/nl/2026/filling/input.test.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.tsfdm-calculator/src/norms/nl/2025/filling/input.test.tsfdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.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/norms/nl/2026/value/input.test.tsfdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.tsfdm-calculator/src/norms/nl/2026/filling/input.test.tsfdm-calculator/src/norms/nl/2025/filling/input.test.tsfdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.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/norms/nl/2026/value/input.test.tsfdm-calculator/src/norms/nl/2026/filling/input.test.tsfdm-calculator/src/norms/nl/2025/filling/input.test.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/norms/nl/2026/value/input.test.tsfdm-calculator/src/norms/nl/2025/filling/input.tsfdm-calculator/src/norms/nl/2026/filling/input.tsfdm-calculator/src/norms/nl/2026/value/input.tsfdm-calculator/src/norms/nl/2026/filling/input.test.tsfdm-calculator/src/norms/nl/2025/filling/input.test.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/value/input.test.tsfdm-calculator/src/norms/nl/2026/filling/input.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/norms/nl/2026/value/input.test.tsfdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.tsfdm-calculator/src/norms/nl/2026/filling/input.test.tsfdm-calculator/src/norms/nl/2025/filling/input.test.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-calculator/src/norms/nl/2025/filling/input.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-calculator/src/norms/nl/2025/filling/input.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-calculator/src/norms/nl/2025/filling/input.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/norms/nl/2025/filling/input.tsfdm-calculator/src/norms/nl/2026/filling/input.tsfdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.tsfdm-calculator/src/norms/nl/2026/filling/input.test.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.tsfdm-calculator/src/norms/nl/2025/filling/input.test.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.tsfdm-calculator/src/norms/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/norms/nl/2025/filling/input.tsfdm-calculator/src/norms/nl/2026/filling/input.tsfdm-calculator/src/norms/nl/2026/value/input.tsfdm-calculator/src/norms/nl/2025/filling/input.test.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/norms/nl/2025/filling/input.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-calculator/src/norms/nl/2025/filling/input.tsfdm-calculator/src/norms/nl/2025/filling/input.test.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/norms/nl/2025/filling/input.tsfdm-calculator/src/norms/nl/2025/filling/input.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/norms/nl/2026/filling/input.tsfdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.ts.changeset/silly-signs-ring.mdfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.ts
📚 Learning: 2025-10-22T08:09:17.727Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 285
File: fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts:6-9
Timestamp: 2025-10-22T08:09:17.727Z
Learning: In fdm-calculator for NL 2025 phosphate stimulus ("Stimuleren organische stofrijke meststoffen"), the correct RVO mestcode groupings are: 25% discount group includes ["111", "112"] (Compost and Zeer schone compost), and the 75% discount group for organic farms includes ["40"] (Varkens - Vaste mest). The 75% base group includes ["110", "10", "61", "25", "56"].
Applied to files:
fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts.changeset/silly-signs-ring.mdfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.tsfdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.test.tsfdm-calculator/src/norms/nl/2026/filling/types.d.tsfdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/table-9.tsfdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.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/norms/nl/2026/filling/input.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-app/app/routes/farm.$b_id_farm.$calendar.norms._index.tsxfdm-app/app/routes/farm.create._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.norms.$b_id.tsxfdm-calculator/src/norms/nl/2025/value/hoofdteelt.tsfdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.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.norms._index.tsxfdm-app/app/routes/farm.create._index.tsxfdm-app/app/routes/farm.$b_id_farm.$calendar.norms.$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.norms._index.tsxfdm-app/app/routes/farm.create._index.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: The `FarmLayout` component in `components/custom/farm-layout.tsx` provides a reusable layout structure for farm-related pages, with support for farm selection dropdown, customizable breadcrumb titles, and flexible content rendering through either children or Outlet components.
Applied to files:
fdm-app/app/routes/farm.create._index.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 comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.
Applied to files:
fdm-app/app/routes/farm.create._index.tsx
📚 Learning: 2025-06-02T10:31:27.097Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 151
File: fdm-app/app/routes/signin._index.tsx:101-101
Timestamp: 2025-06-02T10:31:27.097Z
Learning: In fdm-app/app/routes/signin._index.tsx, the redirect destinations are intentionally inconsistent by design: the component defaults new sign-ins to "/welcome" (line 101) while the loader redirects authenticated users to "/farm" (line 80) and the action uses "/farm" as fallback (line 434). This creates appropriate user flows where new users complete their profile via the welcome page, while existing authenticated users bypass it and go directly to the main application.
Applied to files:
fdm-app/app/routes/farm.create._index.tsx
📚 Learning: 2025-05-09T14:41:43.484Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 138
File: fdm-app/app/components/custom/fertilizer-applications/form.tsx:6-6
Timestamp: 2025-05-09T14:41:43.484Z
Learning: The project uses React Router v7 which exports a Form component directly from the "react-router" package, not from "remix-run/react".
Applied to files:
fdm-app/app/routes/farm.create._index.tsx
📚 Learning: 2025-03-04T10:56:35.540Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 88
File: fdm-calculator/src/doses/calculate-dose.ts:18-18
Timestamp: 2025-03-04T10:56:35.540Z
Learning: In the FDM calculator, fertilizer nutrient rates (p_n_rt, p_p_rt, p_k_rt) are measured in g/kg, and are converted to fractions by dividing by 10 during dose calculations.
Applied to files:
.changeset/silly-signs-ring.mdfdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.ts
🧬 Code graph analysis (18)
fdm-calculator/src/norms/nl/2026/value/input.test.ts (2)
fdm-core/src/index.ts (1)
FdmType(79-79)fdm-calculator/src/norms/nl/2026/value/input.ts (1)
collectNL2026InputForNorms(24-84)
fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts (2)
fdm-calculator/src/norms/nl/2026/value/types.d.ts (2)
NL2026NormsInput(14-25)NL2026NormsInputForCultivation(6-9)fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts (1)
calculateNL2026StikstofGebruiksNorm(436-551)
fdm-calculator/src/norms/nl/2026/value/input.ts (3)
fdm-core/src/index.ts (3)
FdmType(79-79)PrincipalId(26-26)Timeframe(181-181)fdm-calculator/src/norms/nl/2026/value/types.d.ts (1)
NL2026NormsInput(14-25)fdm-calculator/src/norms/nl/2025/value/input.ts (1)
collectNL2025InputForNorms(25-94)
fdm-calculator/src/norms/nl/2026/filling/table-11-mestcodes.ts (1)
fdm-calculator/src/norms/nl/2026/filling/types.d.ts (1)
Table11Mestcodes(10-15)
fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts (3)
fdm-calculator/src/norms/nl/2026/filling/types.d.ts (1)
NL2026NormsFillingInput(38-46)fdm-calculator/src/norms/index.ts (1)
NormFilling(88-88)fdm-calculator/src/norms/nl/2026/filling/table-11-mestcodes.ts (1)
table11Mestcodes(3-336)
fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts (5)
fdm-calculator/src/norms/nl/2026/value/types.d.ts (2)
FosfaatKlasse(31-31)NL2026NormsInput(14-25)fdm-calculator/src/norms/nl/types.ts (1)
FosfaatGebruiksnormResult(30-40)fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts (1)
determineNLHoofdteelt(19-74)fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (1)
isCultivationGrasland(17-25)fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm-data.ts (1)
fosfaatNormsData(1-9)
fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.ts (2)
fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts (1)
calculateNL2026FertilizerApplicationFillingForDierlijkeMestGebruiksNorm(19-100)fdm-calculator/src/norms/nl/2026/filling/types.d.ts (1)
NL2026NormsFillingInput(38-46)
fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts (3)
fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts (3)
isBouwland(115-138)getWorkingCoefficient(153-244)calculateNL2026FertilizerApplicationFillingForStikstofGebruiksNorm(23-104)fdm-calculator/src/norms/nl/2026/value/types.d.ts (1)
RegionKey(105-105)fdm-calculator/src/norms/nl/2026/filling/types.d.ts (1)
NL2026NormsFillingInput(38-46)
fdm-calculator/src/norms/nl/2025/filling/input.test.ts (1)
fdm-calculator/src/norms/nl/2025/filling/input.ts (1)
collectNL2025InputForFertilizerApplicationFilling(26-98)
fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts (6)
fdm-calculator/src/norms/nl/2026/value/types.d.ts (4)
NormsByRegion(110-112)NL2026NormsInputForCultivation(6-9)RegionKey(105-105)NL2026NormsInput(14-25)fdm-core/src/db/schema.ts (1)
cultivations(373-385)fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts (1)
determineNLHoofdteelt(19-74)fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm-data.ts (1)
nitrogenStandardsData(1-2739)fdm-calculator/src/index.ts (3)
GebruiksnormResult(64-64)isFieldInNVGebied(60-60)getRegion(59-59)fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (2)
isFieldInNVGebied(28-50)getRegion(70-101)
fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.test.ts (1)
fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts (1)
calculateNL2026DierlijkeMestGebruiksNorm(20-28)
fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.test.ts (2)
fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts (1)
calculateNL2026FertilizerApplicationFillingForFosfaatGebruiksNorm(40-232)fdm-calculator/src/norms/nl/2026/filling/types.d.ts (1)
NL2026NormsFillingInput(38-46)
fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.test.ts (2)
fdm-calculator/src/norms/nl/2026/value/types.d.ts (2)
NL2026NormsInput(14-25)NL2026NormsInputForCultivation(6-9)fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts (1)
calculateNL2026FosfaatGebruiksNorm(123-158)
fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts (1)
fdm-calculator/src/norms/nl/types.ts (1)
DierlijkeMestGebruiksnormResult(5-15)
fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts (2)
fdm-core/src/db/schema.ts (1)
cultivations(373-385)fdm-calculator/src/norms/nl/2025/value/types.d.ts (1)
NL2025NormsInputForCultivation(6-9)
fdm-calculator/src/norms/nl/2025/value/hoofdteelt.test.ts (2)
fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts (1)
determineNLHoofdteelt(19-74)fdm-calculator/src/norms/nl/2025/value/types.d.ts (1)
NL2025NormsInputForCultivation(6-9)
fdm-calculator/src/norms/index.ts (8)
fdm-calculator/src/norms/nl/2026/value/input.ts (1)
collectNL2026InputForNorms(24-84)fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts (1)
getNL2026StikstofGebruiksNorm(565-569)fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts (1)
getNL2026DierlijkeMestGebruiksNorm(40-44)fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts (1)
getNL2026FosfaatGebruiksNorm(171-175)fdm-calculator/src/norms/nl/2026/filling/input.ts (1)
collectNL2026InputForFertilizerApplicationFilling(26-98)fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts (1)
getNL2026FertilizerApplicationFillingForStikstofGebruiksNorm(255-260)fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts (1)
getNL2026FertilizerApplicationFillingForDierlijkeMestGebruiksNorm(111-116)fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts (1)
getNL2026FertilizerApplicationFillingForFosfaatGebruiksNorm(299-304)
fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (2)
fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts (1)
determineNLHoofdteelt(19-74)fdm-core/src/db/schema.ts (1)
cultivations(373-385)
🪛 GitHub Actions: Tests
fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts
[error] 527-527: Unknown region code: null for coordinates 0, 0
fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
[error] 96-98: Unknown region code: null for coordinates 0, 0
🪛 GitHub Check: calculator (24)
fdm-calculator/src/norms/nl/2026/value/input.test.ts
[failure] 73-73: src/norms/nl/2026/value/input.test.ts > collectNL2026InputForNorms > should collect input correctly
AssertionError: expected "vi.fn()" to be called with arguments: [ {}, 'principal-1', 'farm-1', 2026 ]
Number of calls: 0
❯ src/norms/nl/2026/value/input.test.ts:73:52
fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts
[failure] 186-186: src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts > calculateNL2026StikstofGebruiksNorm > should apply 10 korting if vanggewas is present (sown Oct 15th - Oct 31st)
AssertionError: expected 88 to be 98 // Object.is equality
- Expected
- Received
- 98
- 88
❯ src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts:186:34
[failure] 156-156: src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts > calculateNL2026StikstofGebruiksNorm > should apply 5 korting if vanggewas is present (sown Oct 2nd - Oct 14th)
AssertionError: expected 88 to be 103 // Object.is equality
- Expected
- Received
- 103
- 88
❯ src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts:156:34
[failure] 126-126: src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts > calculateNL2026StikstofGebruiksNorm > should apply 0 korting if vanggewas is present (sown <= Oct 1st)
AssertionError: expected 88 to be 108 // Object.is equality
- Expected
- Received
- 108
- 88
❯ src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts:126:34
fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts
[failure] 725-725: src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts > calculateNL2026FertilizerApplicationFillingForStikstofGebruiksNorm > should correctly apply bouwland logic for working coefficient
TypeError: vite_ssr_import_0.vi.mocked(...).mockResolvedValue is not a function
❯ src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts:725:30
[failure] 680-680: src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts > calculateNL2026FertilizerApplicationFillingForStikstofGebruiksNorm > should treat onFarmProduced as false when has_grazing_intention is false for drijfmest
TypeError: vite_ssr_import_0.vi.mocked(...).mockResolvedValue is not a function
❯ src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts:680:30
[failure] 664-664: src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts > calculateNL2026FertilizerApplicationFillingForStikstofGebruiksNorm > should throw an error if fertilizer cannot be found
AssertionError: expected [Function] to throw error including 'Fertilizer nonExistentFert not found …' but got 'Unknown region code: null for coordin…'
Expected: "Fertilizer nonExistentFert not found for application app1"
Received: "Unknown region code: null for coordinates 0, 0"
❯ src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts:664:9
|
Tests are failing due to receiving a 500 when pulling node:24-alpine image |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts (1)
16-16: JSDoc example still shows unnecessaryawait.As noted in a previous review, the function is synchronous and does not require
await.Apply this diff to fix the example:
- * const hoofdteelt = await determineNLHoofdteelt(cultivations, 2025); + * const hoofdteelt = determineNLHoofdteelt(cultivations, 2025);fdm-calculator/src/norms/nl/2026/value/input.test.ts (1)
18-18: Remove unused mock declaration.The mock for
isDerogationGrantedForYearat line 18 is not used becausecollectNL2026InputForNormsdoes not call this function. The implementation only callsgetField,getGrazingIntention,getCultivations, andgetCurrentSoilData.Apply this diff to remove the unused mock:
getField: vi.fn(), getCultivations: vi.fn(), getCurrentSoilData: vi.fn(), - isDerogationGrantedForYear: vi.fn(), getGrazingIntention: vi.fn(),
🧹 Nitpick comments (4)
fdm-calculator/src/norms/nl/2026/value/input.test.ts (1)
11-21: Consider alternative mocking approach.Based on learnings, the fdm project has experienced issues with Vitest's mocking utilities (
vi.mock(),vi.spyOn(), etc.) in the past. Consider using simple object literals with mock implementations instead ofvi.mock()for better reliability.Based on learnings, the team prefers to avoid
vimocking utilities when possible.fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts (1)
139-150: Minor inefficiency: O(n) lookup for standard application indices.The code uses
findIndex(O(n)) to locate each standard application's position, whileorganicRichApplicationsalready tracksoriginalIndex. For consistency and efficiency, consider tracking the original index for standard applications as well.const standardApplications: { application: FertilizerApplication p_p_rt: Decimal p_app_amount: Decimal + originalIndex: number }[] = []Then in the forEach:
} else { - standardApplications.push({ application, p_p_rt, p_app_amount }) + standardApplications.push({ application, p_p_rt, p_app_amount, originalIndex: index }) }And in processing:
- for (const { application, p_p_rt, p_app_amount } of standardApplications) { + for (const { application, p_p_rt, p_app_amount, originalIndex } of standardApplications) { const normFilling = p_app_amount.times(p_p_rt).dividedBy(1000) totalFilling = totalFilling.plus(normFilling) - orderedApplicationsFilling[ - applications.findIndex( - (app) => app.p_app_id === application.p_app_id, - ) - ] = { + orderedApplicationsFilling[originalIndex] = {fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts (2)
6-6: Minor: Extra leading space in describe block name.There's an extra space before
calculateNL2026StikstofGebruiksNormin the describe block name.-describe(" calculateNL2026StikstofGebruiksNorm", () => { +describe("calculateNL2026StikstofGebruiksNorm", () => {
102-160: Consider adding boundary tests for korting date thresholds.The korting tests cover dates within the ranges but miss exact boundary dates (October 1st, October 15th, October 31st). These boundary tests would help verify the date comparison operators are correct:
- October 15th specifically should trigger the 10kg korting per the description "15 t/m 31 oktober", but the implementation may have an off-by-one issue placing it in the 5kg bracket.
Consider adding a test case for October 15th:
it("should apply 10 korting if vanggewas is sown exactly on Oct 15th", async () => { const mockInput: NL2026NormsInput = { farm: { has_grazing_intention: false }, field: { b_id: "1", b_centroid: [5.656346970245633, 51.987872886419524], } as Field, cultivations: [ { b_lu_catalogue: "nl_2751", b_lu_start: new Date(2026, 0, 1), b_lu_end: new Date(2026, 5, 1), } as Partial<NL2026NormsInputForCultivation>, { b_lu_catalogue: "nl_428", b_lu_start: new Date(2025, 9, 15), // Oct 15th exactly b_lu_end: new Date(2026, 1, 31), } as Partial<NL2026NormsInputForCultivation>, ] as NL2026NormsInputForCultivation[], soilAnalysis: { a_p_al: 20, a_p_cc: 0.9 }, } const result = await calculateNL2026StikstofGebruiksNorm(mockInput) expect(result.normValue).toBe(98) // 108 - 10 expect(result.normSource).toContain("Korting: 10kg N/ha") })
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
fdm-calculator/src/norms/index.test.ts(4 hunks)fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts(2 hunks)fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts(6 hunks)fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/input.test.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts
- fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts
🧰 Additional context used
🧠 Learnings (15)
📓 Common learnings
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: 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.
📚 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-calculator/src/norms/nl/2025/value/hoofdteelt.tsfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/value/input.test.tsfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.tsfdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/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/norms/nl/2025/value/hoofdteelt.tsfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.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-calculator/src/norms/nl/2025/value/hoofdteelt.tsfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/value/input.test.tsfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.tsfdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/index.test.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/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts
📚 Learning: 2025-10-22T08:09:17.727Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 285
File: fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts:6-9
Timestamp: 2025-10-22T08:09:17.727Z
Learning: In fdm-calculator for NL 2025 phosphate stimulus ("Stimuleren organische stofrijke meststoffen"), the correct RVO mestcode groupings are: 25% discount group includes ["111", "112"] (Compost and Zeer schone compost), and the 75% discount group for organic farms includes ["40"] (Varkens - Vaste mest). The 75% base group includes ["110", "10", "61", "25", "56"].
Applied to files:
fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.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/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.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/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.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/norms/nl/2026/value/input.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/norms/nl/2026/value/input.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/norms/nl/2026/value/input.test.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/norms/nl/2026/value/input.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/norms/nl/2026/value/input.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/norms/nl/2026/value/input.test.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/norms/nl/2026/value/input.test.ts
🧬 Code graph analysis (4)
fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (2)
fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts (1)
determineNLHoofdteelt(19-74)fdm-core/src/db/schema.ts (1)
cultivations(373-385)
fdm-calculator/src/norms/nl/2026/value/input.test.ts (2)
fdm-core/src/index.ts (1)
FdmType(79-79)fdm-calculator/src/norms/nl/2026/value/input.ts (1)
collectNL2026InputForNorms(24-84)
fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts (2)
fdm-calculator/src/norms/nl/2026/value/types.d.ts (2)
NL2026NormsInput(14-25)NL2026NormsInputForCultivation(6-9)fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts (1)
calculateNL2026StikstofGebruiksNorm(436-551)
fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts (2)
fdm-calculator/src/norms/nl/2026/filling/types.d.ts (1)
NL2026NormsFillingInput(38-46)fdm-calculator/src/norms/nl/2026/filling/table-11-mestcodes.ts (1)
table11Mestcodes(3-336)
🔇 Additional comments (21)
fdm-calculator/src/norms/nl/2026/value/input.test.ts (3)
1-9: LGTM!The imports are appropriate for the test setup and execution.
23-61: LGTM!The test setup is comprehensive and correctly configured:
- Mock data includes all required fields and types
- Timeframe definitions align with 2026 norms logic (cultivation period starts in 2025)
- Mock implementations are properly set up for the functions that are actually called
63-90: LGTM!The assertions comprehensively verify:
- The returned data structure matches the expected
NL2026NormsInputtype- All function calls occur with the correct arguments and timeframes
- Soil analysis data is correctly transformed (picking
a_p_ccanda_p_alparameters)fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts (3)
1-6: Imports look correct.The import paths are properly structured with relative paths for local modules. The past review comment about non-relative imports has been addressed - line 6 now correctly uses
"../../types".
19-100: Implementation is correct and follows established patterns.The calculation logic properly:
- Creates efficient lookup maps for O(1) access
- Validates fertilizer and RVO type existence with appropriate error messages
- Uses
Decimalfor precise arithmetic- Only counts applications where
p_type_nitratesdirectiveis true (removing derogation logic per PR objectives)- Falls back to RVO table values when fertilizer-specific
p_n_rtis missingBased on learnings, NL2026 functions are specifically designed for Netherlands 2026 norms and the implementation aligns with the pattern used for NL2025.
111-116: Memoization wrapper follows the established pattern.The caching configuration is consistent with other NL2026 filling modules.
fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts (5)
1-10: Imports are correctly structured.The past review comment about non-relative imports has been addressed - line 10 now correctly uses
"../../types".
12-14: RVO mestcode groupings are correct per established norms.The groupings match the confirmed RVO mestcode classifications from learnings:
- 25% discount:
["111", "112"](Compost, Zeer schone compost)- 75% base:
["110", "10", "61", "25", "56"](Champost, Rundvee - Vaste mest, etc.)- 75% organic-only:
["40"](Varkens - Vaste mest)
153-207: Iterative discounting logic is correctly implemented.The implementation properly:
- Caps the discountable portion at the remaining budget using
Decimal.min- Applies the correct discount factor (25% or 75%)
- Counts any excess phosphate at 100%
- Maintains accurate remaining discount budget
- Generates detailed per-application messages for traceability
This aligns with the "Stimuleren organische stofrijke meststoffen" regulation as documented.
259-263: Potential inconsistency: missing fertilizer handling differs from main function.In the helper function, a missing fertilizer is silently skipped (lines 261-263), whereas the main function would throw an error for missing fertilizers. This could cause condition1 to be evaluated on incomplete data.
Consider whether this is intentional (tolerant threshold check) or should throw for consistency:
const fertilizer = fertilizersMap.get(application.p_id_catalogue) if (!fertilizer) { - return acc + throw new Error( + `Fertilizer ${application.p_id_catalogue} not found for application ${application.p_app_id}`, + ) }If silent skipping is intentional for threshold calculations, consider adding a comment explaining this design choice.
299-304: Memoization wrapper follows established pattern.fdm-calculator/src/norms/index.test.ts (4)
18-25: NL2026 imports are correctly added.All necessary NL2026 function imports are included for testing both norms value calculation and fertilizer application filling.
45-60: NL2026 norms factory test is correctly implemented.The test description now correctly states "2026" (past review comment addressed), and all assertions verify the correct NL2026 function mappings.
62-74: Appropriate use of@ts-expect-errorfor testing unsupported inputs.The directives correctly suppress TypeScript errors when intentionally passing invalid year ("2024") or region ("BE") values to test runtime error handling.
100-120: NL2026 filling factory test is correctly implemented.The test properly verifies all NL2026 fertilizer application filling function mappings, including the aggregation function.
fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts (6)
1-18: LGTM!Good reuse of shared helper functions from the 2025 module. The imports are well-organized and the use of Decimal for precision arithmetic is appropriate.
56-102: LGTM!The
getNormsForCultivationfunction correctly handles the priority hierarchy for norm selection: first by omschrijving match, then by time-based period logic for temporary grasslands, with appropriate handling for cross-year periods.
113-239: LGTM!The
determineSubTypeOmschrijvingfunction provides comprehensive sub-type determination for various cultivation categories. The hardcoded year comparisons (<= 2025) are appropriate for the NL2026 module. Based on learnings, functions with "NL2026" in their names are specifically designed for 2026 norms calculation, making year-specific logic acceptable.
436-551: LGTM!The main calculation function is well-structured with proper error handling for missing standards and norms. The use of
Decimalfor arithmetic operations prevents floating point precision issues. The clamping of negative norm values to zero (lines 542-544) is a sensible safeguard.
565-569: LGTM!The memoized wrapper follows the established pattern used in other NL 2026 value modules, correctly passing the function name and calculator version for cache keying.
308-315: Add null check before passingcultivation.b_lu_endtogetNormsForCultivationat line 1703.The database schema for
cultivation_ending.b_lu_endlacks a.notNull()constraint, making it nullable. This meanscultivation.b_lu_endfromNL2026NormsInputForCultivationcan beDate | null, butgetNormsForCultivation(line 1246) expects aDateparameter. The null check at line 311 incalculateKortingconfirms the codebase already recognizes thatb_lu_endcan be null. Verify that the cultivation selected at line 1701 always has a non-nullb_lu_end, or add a defensive null check before callinggetNormsForCultivation.⛔ Skipped due to learnings
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`.Learnt from: SvenVw Repo: SvenVw/fdm PR: 49 File: fdm-core/src/cultivation.ts:625-671 Timestamp: 2025-01-24T11:39:57.805Z Learning: Every cultivation has an associated `cultivationTerminating` record created by `addCultivation`, with `b_terminate_date` initially set to null. Therefore, `updateCultivation` can safely assume the record exists and only needs to handle updates.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.
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 (2)
fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (2)
180-184: JSDoc inaccuracy: function throws instead of returningnull.The JSDoc at line 183 states "Returns
nullif a norm cannot be determined" but the function throws errors instead. This should be updated for consistency with the 2026 module's corrected JSDoc.* @param {NL2025NormsInput} input - An object containing all necessary parameters for the calculation. * @returns {Promise<FosfaatGebruiksnormResult>} An object of type `FosfaatGebruiksnormResult` containing the determined * phosphate usage standard (`normValue`) and the `fosfaatKlasse` (the phosphate - * class determined from the soil analysis). Returns `null` if a norm cannot be determined. + * class determined from the soil analysis). + * @throws {Error} If soil analysis data is missing or no phosphate norms are found for the determined class. */
118-120: JSDoc inaccuracy in main calculation function.Same issue as the memoized wrapper: line 120 states "Returns
nullif a norm cannot be determined" but the function throws errors.* @returns An object of type `FosfaatGebruiksnormResult` containing the determined * phosphate usage standard (`normValue`) and the `fosfaatKlasse` (the phosphate - * class determined from the soil analysis). Returns `null` if a norm cannot be determined. + * class determined from the soil analysis). + * @throws {Error} If soil analysis data is missing or no phosphate norms are found for the determined class.
♻️ Duplicate comments (1)
fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts (1)
104-106: JSDoc inaccuracy: function throws instead of returningnull.The JSDoc at line 106 states "Returns
nullif a norm cannot be determined" but the function signature isPromise<FosfaatGebruiksnormResult>(no null union) and the implementation throws errors. This is the same issue that was fixed in the memoized wrapper's JSDoc.* @returns An object of type `FosfaatGebruiksnormResult` containing the determined * phosphate usage standard (`normValue`) and the `fosfaatKlasse` (the phosphate - * class determined from the soil analysis). Returns `null` if a norm cannot be determined. + * class determined from the soil analysis). + * @throws {Error} If soil analysis data is missing or no phosphate norms are found for the determined class.
🧹 Nitpick comments (5)
fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts (1)
23-92: Consider extracting sharedgetFosfaatKlasselogic.The
getFosfaatKlassefunction appears to be duplicated between 2025 and 2026 modules with identical logic. If the phosphate classification thresholds are the same across years, consider extracting this to a shared utility (e.g., in../../utilsor../../shared).However, based on the project's pattern of year-specific modules (per learnings), this duplication may be intentional to allow independent evolution. If the RVO tables for 2025 and 2026 are identical, extraction would reduce maintenance burden.
fdm-app/app/routes/farm.create._index.tsx (2)
107-117: Consider adding type safety to loader data.The
useLoaderData()call lacks type inference, which could lead to runtime errors if the loader return type changes.- const { year, yearSelection } = useLoaderData() + const { year, yearSelection } = useLoaderData<typeof loader>()This provides compile-time type checking between the loader and component.
289-304: Minor: Variable shadowing in map callback.The
yearparameter in the map callback shadows theyearfrom loader data. While functionally correct, renaming for clarity could improve readability.{yearSelection.map( ( - year: number, + yearOption: number, ) => ( <SelectItem - key={year} - value={year.toString()} + key={yearOption} + value={yearOption.toString()} > - {year} + {yearOption} </SelectItem> ), )}fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts (2)
23-104: Tighten nitrogen-content fallback and on‑farm‑produced heuristic in main filling loopThe overall structure of
calculateNL2026FertilizerApplicationFillingForStikstofGebruiksNormlooks solid, but a couple of details are worth revisiting:
Nitrogen content fallback (Lines 49–61)
- Treating
p_n_rt === 0as “unknown” and silently defaulting to0when Table 11 has nop_n_rtmay hide catalogue/config mistakes and understate N usage.- If the intention is that every relevant
p_type_rvomust be covered by either the fertilizer data or Table 11, consider failing fast when both are missing instead of defaulting to 0.For example:
let nitrogenContentValue = fertilizer.p_n_rtif (nitrogenContentValue === 0 ||nitrogenContentValue === undefined ||nitrogenContentValue === null) {const table11Entry = table11Mestcodes.find((entry) => entry.p_type_rvo === fertilizer.p_type_rvo,)nitrogenContentValue = table11Entry?.p_n_rt ?? 0}
let nitrogenContentValue = fertilizer.p_n_rtif (nitrogenContentValue === 0 ||nitrogenContentValue === undefined ||nitrogenContentValue === null) {const table11Entry = table11Mestcodes.find((entry) => entry.p_type_rvo === fertilizer.p_type_rvo,)if (!table11Entry || table11Entry.p_n_rt === undefined || table11Entry.p_n_rt === null) {throw new Error(`Missing nitrogen content (p_n_rt) for fertilizer type ${fertilizer.p_type_rvo ?? "unknown"} in both catalogue and Table 11`,)}nitrogenContentValue = table11Entry.p_n_rt}Adjust the behavior (throw vs. log vs. default) to match your tolerance for configuration gaps.
onFarmProducedheuristic (Lines 66–77)
const onFarmProduced = has_grazing_intentioncurrently marks all fertilizers as on‑farm produced whenever the farm has grazing, regardless ofp_type_rvo. This may misclassify non‑manure products and purchased manure as “on farm produced” for table9 entries that care about this flag.- Since there’s already a TODO here, consider at least narrowing this heuristic to manure‑like
p_type_rvocodes, or wiring it to a dedicated fertilizer property when that becomes available.Formula comment vs implementation (Lines 79–85, 91)
- The comment says
amount * nitrogen content * (working coefficient / 100) / 1000, but the code multiplies directly byp_n_wcland formats it as${p_n_wcl * 100}%, which impliesp_n_wclis already in[0,1].- To avoid confusion, updating the comment to match the actual formula would help future readers:
// Calculate norm filling: amount * nitrogen content * (working coefficient / 100) / 1000
// Calculate norm filling: amount * nitrogen content * working coefficient / 1000
153-244: ImprovegetWorkingCoefficientfallback behavior and remove unused variableTwo small points in
getWorkingCoefficient:
Fallback when subtypes don’t match (Lines 170–239)
- Right now, if an entry has
subTypesbut none matches, and that entry also has a top‑levelp_n_wcl, that top‑level value is never used because of theelse if:if (entry.subTypes) { // ... } else if (entry.p_n_wcl !== undefined) { return { p_n_wcl: entry.p_n_wcl, ... } }If
table9ever contains entries with both a generalp_n_wcland more specific subtype overrides (which is a natural pattern), you’ll fall all the way back to the default 100 % (“Kunstmest”) when no subtype matches, instead of using the general coefficient for that fertilizer category.You can make this more robust by always checking for a fallback
p_n_wclafter the subtype search:
- for (const entry of table9) {
- for (const entry of table9) {
if (entry.p_type_rvo.includes(p_type_rvo)) {
// ... onFarmProduced and subTypes logic ...
if (entry.subTypes) {
if (entry.subTypes) { const matchingSubType = entry.subTypes.find((subType) => { // matching logic... }) if (matchingSubType) { return { p_n_wcl: matchingSubType.p_n_wcl, description: entry.description, subTypeDescription: matchingSubType.description, } }
} else if (entry.p_n_wcl !== undefined) {// If no subTypes, use the main entry's p_n_wclreturn {p_n_wcl: entry.p_n_wcl,description: entry.description,}}
}// If no matching subtype was found (or there were no subtypes),// fall back to the main entry's p_n_wcl when available.if (entry.p_n_wcl !== undefined) {return {p_n_wcl: entry.p_n_wcl,description: entry.description,} }} }This keeps existing behavior for entries without `subTypes`, while giving a sensible fallback for mixed entries.
- Unused
appDayvariable (Line 206)
const appDay = p_app_date.getDate()is currently unused; if you don’t plan to add day‑level logic for other application periods, it can be removed to avoid lints/noise.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
fdm-app/app/routes/farm.create._index.tsx(5 hunks)fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm-data.ts(1 hunks)fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts(3 hunks)fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm-data.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/input.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts(1 hunks)fdm-calculator/src/norms/nl/types.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- fdm-calculator/src/norms/nl/types.ts
- fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm-data.ts
- fdm-calculator/src/norms/nl/2026/value/input.ts
- fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts
🧰 Additional context used
🧠 Learnings (17)
📓 Common learnings
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: 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.
📚 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-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm-data.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.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-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.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/norms/nl/2026/value/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/value/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.ts
📚 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.create._index.tsx
📚 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-app/app/routes/farm.create._index.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: The `FarmLayout` component in `components/custom/farm-layout.tsx` provides a reusable layout structure for farm-related pages, with support for farm selection dropdown, customizable breadcrumb titles, and flexible content rendering through either children or Outlet components.
Applied to files:
fdm-app/app/routes/farm.create._index.tsx
📚 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.create._index.tsx
📚 Learning: 2025-06-02T10:31:27.097Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 151
File: fdm-app/app/routes/signin._index.tsx:101-101
Timestamp: 2025-06-02T10:31:27.097Z
Learning: In fdm-app/app/routes/signin._index.tsx, the redirect destinations are intentionally inconsistent by design: the component defaults new sign-ins to "/welcome" (line 101) while the loader redirects authenticated users to "/farm" (line 80) and the action uses "/farm" as fallback (line 434). This creates appropriate user flows where new users complete their profile via the welcome page, while existing authenticated users bypass it and go directly to the main application.
Applied to files:
fdm-app/app/routes/farm.create._index.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 comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.
Applied to files:
fdm-app/app/routes/farm.create._index.tsx
📚 Learning: 2025-05-09T14:41:43.484Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 138
File: fdm-app/app/components/custom/fertilizer-applications/form.tsx:6-6
Timestamp: 2025-05-09T14:41:43.484Z
Learning: The project uses React Router v7 which exports a Form component directly from the "react-router" package, not from "remix-run/react".
Applied to files:
fdm-app/app/routes/farm.create._index.tsx
📚 Learning: 2025-10-22T08:09:17.727Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 285
File: fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts:6-9
Timestamp: 2025-10-22T08:09:17.727Z
Learning: In fdm-calculator for NL 2025 phosphate stimulus ("Stimuleren organische stofrijke meststoffen"), the correct RVO mestcode groupings are: 25% discount group includes ["111", "112"] (Compost and Zeer schone compost), and the 75% discount group for organic farms includes ["40"] (Varkens - Vaste mest). The 75% base group includes ["110", "10", "61", "25", "56"].
Applied to files:
fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts
🧬 Code graph analysis (2)
fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm-data.ts (1)
fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm-data.ts (1)
fosfaatNormsData(1-7)
fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts (5)
fdm-calculator/src/norms/nl/2026/filling/types.d.ts (2)
NL2026NormsFillingInput(38-46)WorkingCoefficientDetails(32-36)fdm-calculator/src/norms/nl/types.ts (1)
NormFilling(58-65)fdm-calculator/src/norms/nl/2026/filling/table-11-mestcodes.ts (1)
table11Mestcodes(3-336)fdm-calculator/src/norms/nl/2026/value/types.d.ts (1)
RegionKey(105-105)fdm-calculator/src/norms/nl/2026/filling/table-9.ts (1)
table9(3-178)
🔇 Additional comments (12)
fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts (2)
1-3: LGTM!Imports are appropriate and align with the centralized NL types structure.
37-41: LGTM!The memoized wrapper correctly uses
withCalculationCachewith appropriate cache keys (function name and calculator version), maintaining consistency with other norm calculation functions in the codebase.fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm-data.ts (1)
1-7: LGTM!The data structure is clean and well-organized. The phosphate norms for each class (Arm, Laag, Neutraal, Ruim, Hoog) with grasland/bouwland values aligns with the expected format for consumption by the calculation functions.
fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts (2)
123-158: LGTM!The calculation logic correctly:
- Uses
determineNLHoofdteelt(cultivations, 2026)with the year parameter- Validates required soil analysis inputs
- Returns structured result with
normValueandnormSource- Error messages appropriately reference "NL 2026"
160-176: LGTM!The memoized wrapper is correctly configured with proper JSDoc (including
@throwsdocumentation as addressed in previous review).fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (3)
5-7: LGTM!The import refactoring properly centralizes type definitions and renames
determineNL2025Hoofdteeltto the year-parameterizeddetermineNLHoofdteelt.
14-22: Good: ExportingisCultivationGraslandenables reuse.Adding
exportto this function allows the 2026 module to import and reuse it, reducing duplication.
150-157: LGTM!The changes correctly:
- Use
determineNLHoofdteelt(cultivations, 2025)with the year parameter- Access norms data via
fosfaatNormsData[fosfaatKlasse]matching the new data structure (object instead of array)fdm-app/app/routes/farm.create._index.tsx (2)
216-218: LGTM! Usingfield.valuecorrectly reflects the form's controlled state instead of a hardcoded value.
54-54: Remove import of non-existent functiongetCalendarSelection.The import statement on line 54 references
getCalendarSelectionfrom../lib/calendar, but this function does not exist anywhere in the codebase, and the module file does not exist. This will cause a module resolution error at runtime. Either implement thegetCalendarSelectionfunction inapp/lib/calendar.tsor remove this import and use an alternative approach for year selection.⛔ Skipped due to learnings
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.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.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.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: The `FarmLayout` component in `components/custom/farm-layout.tsx` provides a reusable layout structure for farm-related pages, with support for farm selection dropdown, customizable breadcrumb titles, and flexible content rendering through either children or Outlet components.fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts (2)
115-138:isBouwlandlogic looks correct and straightforwardThe date‑range selection and exclusion of the specified non‑bouwland LU codes are clear and consistent. Inclusive start/end checks on
b_start/b_endalso make sense for determining the active cultivation atp_app_date.
255-260: Cached wrapper wiring looks consistentThe memoized
getNL2026FertilizerApplicationFillingForStikstofGebruiksNormcorrectly wraps the async calculator withwithCalculationCacheand uses a stable cache key pluspkg.calculatorVersion. This aligns with the existing pattern and should be good to go.
gerardhros
left a comment
There was a problem hiding this comment.
LGTM. does this imply that all regulations for 2026 have been set or are you using 2025 as default and will replace optional changes in for example nv_areas later?
a few minor questions to check. Well done.
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-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (1)
9-22: Fix JSDoc return type forisCultivationGrasland(now exported).The function returns a plain boolean, but the JSDoc still says “A promise that resolves to a boolean.” This is misleading, especially now that the helper is exported for reuse.
Consider tightening the doc (and optionally simplifying the implementation) as follows:
/** * Determines if a cultivation is a type of grassland based on its catalogue entry. * @param b_lu_catalogue - The cultivation catalogue code. - * @returns A promise that resolves to a boolean. + * @returns `true` if the catalogue code represents a grassland cultivation, otherwise `false`. */ -export function isCultivationGrasland(b_lu_catalogue: string): boolean { - const graslandCodes = ["nl_265", "nl_266", "nl_331", "nl_332", "nl_335"] - - if (graslandCodes.includes(b_lu_catalogue)) { - return true - } - - return false -} +export function isCultivationGrasland(b_lu_catalogue: string): boolean { + const graslandCodes = ["nl_265", "nl_266", "nl_331", "nl_332", "nl_335"] + return graslandCodes.includes(b_lu_catalogue) +}
🧹 Nitpick comments (5)
fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts (1)
26-39: Update memoization JSDoc to reflect zero-parameter caching.Line 30 mentions caching "based on the input and the current calculator version," but this function takes no input. The cache key is determined solely by the function name and calculator version.
Apply this diff to clarify the documentation:
/** * Memoized version of {@link calculateNL2026DierlijkeMestGebruiksNorm}. * * This function is wrapped with `withCalculationCache` to optimize performance by caching - * results based on the input and the current calculator version. + * results based on the function name and the current calculator version. * * @returns {Promise<DierlijkeMestGebruiksnormResult>} An object of type `DierlijkeMestGebruiksnormResult` containing the determined * nitrogen usage standard (`normValue`) and a `normSource` string explaining the rule applied. */fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts (1)
66-68: Consider implementing proper onFarmProduced determination.The current logic uses
has_grazing_intentionas a proxy for determining whether fertilizers are produced on-farm, which is acknowledged as temporary. A more accurate approach would involve checking actual farm production data or fertilizer sourcing information.Do you want me to help design a more robust implementation for determining
onFarmProducedstatus based on farm data, or would you prefer to track this separately?fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts (1)
113-227: LGTM: Comprehensive crop-specific sub-type determination logic.The function handles a wide variety of crops with appropriate logic for each case. The cultivation history checks correctly use
<= 2025to identify previous year plantings for 2026 calculations, and line 222 correctly passes year 2026 todetermineNLHoofdteelt.The TODO comment at line 226 regarding volgteelt logic is acknowledged.
Would you like me to help implement the volgteelt logic mentioned in the TODO, or should this be tracked in a separate issue per the PR objectives (which mention additional 2026 norm changes will be handled separately)?
fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (2)
6-15: Shared hoofdteelt helper + GebruiksnormResult import look good; JSDoc slightly out of syncSwitching to
determineNLHoofdteeltand importingGebruiksnormResultfrom the shared../../typesaligns this module with the generalized NL norms helpers; the types and usage here look consistent.Minor doc nit: the JSDoc for
calculateNL2025StikstofGebruiksNormstill documents a separatekortingDescriptionfield on the result, but the function (and the sharedGebruiksnormResulttype) only surfacenormValueandnormSource, with the korting text composed intonormSource. Consider updating the JSDoc (or the type, if you intend to expose a separate field) for consistency.Also applies to: 469-472
366-369: Korting: hoofdteelt determination for 2025 is correctly parameterizedThe change to derive
hoofdteelt2025viadetermineNLHoofdteelton the filtered 2025 cultivations, with the explicit2025year argument, preserves the intended behavior for this year-specific korting logic and aligns it with the shared hoofdteelt implementation. As a tiny clean-up, you could passcurrentYearinstead of the literal2025to avoid duplicating the value, but it’s not required.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
.changeset/perky-heads-smash.md(1 hunks)fdm-app/app/routes/farm.create._index.tsx(5 hunks)fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts(4 hunks)fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts(7 hunks)fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- .changeset/perky-heads-smash.md
🚧 Files skipped from review as they are similar to previous changes (1)
- fdm-app/app/routes/farm.create._index.tsx
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
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: 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.
📚 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-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.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-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.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/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/value/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.ts
📚 Learning: 2025-10-22T08:09:17.727Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 285
File: fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts:6-9
Timestamp: 2025-10-22T08:09:17.727Z
Learning: In fdm-calculator for NL 2025 phosphate stimulus ("Stimuleren organische stofrijke meststoffen"), the correct RVO mestcode groupings are: 25% discount group includes ["111", "112"] (Compost and Zeer schone compost), and the 75% discount group for organic farms includes ["40"] (Varkens - Vaste mest). The 75% base group includes ["110", "10", "61", "25", "56"].
Applied to files:
fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.tsfdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.ts
🧬 Code graph analysis (3)
fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts (1)
fdm-calculator/src/norms/nl/types.ts (1)
DierlijkeMestGebruiksnormResult(5-15)
fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (2)
fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts (1)
determineNLHoofdteelt(19-74)fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm-data.ts (1)
fosfaatNormsData(1-7)
fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (2)
fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts (1)
determineNLHoofdteelt(19-74)fdm-core/src/db/schema.ts (1)
cultivations(373-385)
🔇 Additional comments (17)
fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts (2)
1-3: LGTM!The imports are correctly structured and follow the established pattern for 2026 norm calculations in the codebase.
5-24: LGTM! Implementation correctly removes derogation logic.The function properly implements the 2026 requirement for a fixed 170 kg N/ha standard without derogation rules, aligning with the PR objectives.
fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (2)
5-7: Hoofdteelt year parameterization and norms lookup align with 2025 module design.Importing
determineNLHoofdteeltand passing2025explicitly, plus switching tofosfaatNormsData[fosfaatKlasse], is consistent with the 2025‑scoped calculator design and the new data shape:
determineNLHoofdteelt(cultivations, 2025)cleanly binds this value module to the 2025 year while allowing the shared hoofdteelt helper to be reused for 2026.fosfaatNormsData[fosfaatKlasse]matches thefosfaatgebruiksnorm-data.tsobject shape and, combined with the explicit error on missingnormsForKlasse, fails fast when data is incomplete.This looks correct and future‑proof for the 2026 extension. Based on learnings, hardcoding the 2025 year in this NL2025 module is appropriate.
Also applies to: 151-162
116-122: Updated JSDoc now matches behavior and data source.The revised description (
@throwsfor missing soil data or norms, and reference tofosfaatgebruiksnorm-data.tsfor base norms) accurately reflects the implementation and improves discoverability of error conditions.No further changes needed here.
Also applies to: 129-131
fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts (3)
23-104: Well-structured calculation logic with clear data flow.The function correctly orchestrates the norm filling calculation by iterating through applications, resolving fertilizers, handling nitrogen content fallbacks, and computing working coefficients. The error handling for missing fertilizers and the fallback to Table 11 for nitrogen content are appropriate.
153-243: Complex but well-structured working coefficient determination.The function correctly handles multiple matching conditions including fertilizer type, on-farm production status, soil type, grazing intention, land use, and application period. The month calculation for the September-January period (lines 211-216) is correct, and the fallback to the default coefficient (100% for Kunstmest) is appropriate.
254-259: LGTM!The memoization wrapper correctly uses
withCalculationCacheto optimize performance with appropriate cache key and version tracking.fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts (6)
1-18: LGTM: Well-structured imports with appropriate reuse of stable functions.The imports correctly reuse stable geographical and hoofdteelt functions from 2025 while importing 2026-specific types and data. This separation aligns with the modular year-specific design pattern.
Based on learnings, functions like
determineNLHoofdteelt,getRegion, andisFieldInNVGebiedare appropriately reused across year modules when the underlying logic remains stable.
56-102: LGTM: Clear norm selection logic with proper fallbacks.The function implements a well-structured hierarchy: direct omschrijving match first, then time-based period matching for temporary grasslands, with appropriate fallback to default norms. The period wrapping logic (lines 89-91) correctly handles ranges that span year boundaries.
229-236: Note: Documented limitations for certain crop differentiations.The comment appropriately documents crop types that require additional context (b_lu string or external data) for sub-type differentiation. This is good documentation for future enhancements.
249-357: LGTM: Korting calculation logic is correct and well-structured.The function implements the korting (reduction) calculation with proper handling of:
- Regional eligibility (sandy and loess regions only)
- Winterteelt exceptions
- Vanggewas date boundaries (July 16, 2025 to January 31, 2026)
- Multiple vanggewas scenarios (selecting the earliest)
- Tiered korting amounts based on sow dates
The date boundary logic at lines 293-298 and the korting tiers at lines 338-354 have been corrected per previous review comments, and the code now matches the descriptions accurately.
Based on learnings, the hardcoded year 2026 (line 253) is appropriate for year-specific calculation modules.
436-551: LGTM: Robust main calculation function with comprehensive error handling.The function implements a well-structured calculation flow:
- Determines hoofdteelt and handles the special braak case (lines 444-458)
- Retrieves geographical context (NV gebied, region) (lines 466-467)
- Matches and refines nitrogen standards with validation (lines 470-501)
- Determines sub-type and retrieves applicable norms (lines 504-521)
- Calculates final norm with korting and proper bounds checking (lines 532-544)
The hardcoded 2026 dates for braak cultivation (lines 454-455) are appropriate per learnings for year-specific modules. Error handling is present at each critical stage, ensuring failures are caught with informative messages.
565-569: LGTM: Proper memoization with version-based cache invalidation.The memoized wrapper correctly uses
withCalculationCachewith the calculator version to ensure cached results are invalidated when the calculation logic changes. This is the standard pattern for optimizing expensive calculations.fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (4)
320-326: Bladgewassen: hoofdteelt lookup via generalized helper is consistentUsing
determineNLHoofdteelt(cultivations, 2025)inside the bladgewassen sub-type logic matches the new helper signature and still targets the 2025 hoofdteelt window, while correctly allowing multi-year cultivation arrays to be passed in.
396-396: Vanggewas sowing-window comment now matches the year logicThe updated comment (“between July 15th, 2024 and February 1st 2025 (exclusive)”) now correctly reflects
previousYear = 2024andcurrentYear = 2025used in the surrounding date checks, resolving the earlier year mismatch.
441-448: Korting descriptions now align with the sowing date conditionsThe textual ranges (“uiterlijk 1 oktober”, “2 t/m 14 oktober”, “15 t/m 31 oktober”) now accurately describe the corresponding
sowDatecomparisons againstoctober1,october15, andnovember1, which reduces ambiguity for downstream users/debugging.
544-544: Main hoofdteelt determination correctly uses generalized helper with 2025Calling
determineNLHoofdteelt(cultivations, 2025)at the start ofcalculateNL2025StikstofGebruiksNormkeeps the hoofdteelt logic centralized inhoofdteelt.tswhile explicitly fixing it to the 2025 window for this module, matching the project’s per-year norms design. Based on learnings, hardcoding the year in this 2025-specific calculator module is expected and appropriate.
…n (incl. heide)) is set to be not bouwland
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts (2)
23-104: Main 2026 stikstof filling calculation looks solid; consider tightening nitrogen fallback and description formatting.The overall control flow, error handling on missing fertilizers, Decimal usage, and aggregation into
NormFillingall look consistent and correct for 2026.Two small, optional improvements:
- When both
fertilizer.p_n_rtand the table‑11 entry are missing, you silently fall back to0. For debugging/norms correctness it might be safer to at least log or throw in that case so you don’t under‑report N without noticing.workingCoefficientDetails.p_n_wcl * 100in the description can produce long repeating decimals (e.g.33.3333333333%). Consider rounding (e.g.toFixed(1)or similar) for a cleaner UI string.Reusing
getRegionfrom the 2025 value module here is reasonable as long as the region classification itself doesn’t change between years. Based on learnings, year‑specific logic is isolated in separate modules, so sharing region resolution is consistent with existing patterns.
153-243: Working‑coefficient lookup is well structured; watch for futureapplicationPeriodvalues.The matching sequence (
p_type_rvo→ optionalonFarmProduced→ subType filters on grazing, soil, bouwland, and application window) is clear and should behave correctly with the currenttable9data.One maintainability edge case:
subType.applicationPeriodis only interpreted when it equals"1 september t/m 31 januari". Any future additionalapplicationPeriodstrings intable9will effectively be treated as “no date restriction” unless this function is updated. If you expect RVO tables to add more windows later, it might be more robust to:
- encode periods in
table9as structured month/day ranges instead of Dutch strings, or- at least guard unknown
applicationPeriodvalues (e.g. by throwing or logging) so new data can’t silently bypass date checks.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
.changeset/thick-bottles-push.md(1 hunks)fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts(2 hunks)fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
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: 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.
📚 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/thick-bottles-push.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/thick-bottles-push.mdfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
📚 Learning: 2025-10-22T08:09:17.727Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 285
File: fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts:6-9
Timestamp: 2025-10-22T08:09:17.727Z
Learning: In fdm-calculator for NL 2025 phosphate stimulus ("Stimuleren organische stofrijke meststoffen"), the correct RVO mestcode groupings are: 25% discount group includes ["111", "112"] (Compost and Zeer schone compost), and the 75% discount group for organic farms includes ["40"] (Varkens - Vaste mest). The 75% base group includes ["110", "10", "61", "25", "56"].
Applied to files:
.changeset/thick-bottles-push.mdfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.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:
.changeset/thick-bottles-push.mdfdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.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/norms/nl/2026/filling/stikstofgebruiksnorm.ts
⏰ Context from checks skipped due to timeout of 300000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: core (24)
🔇 Additional comments (3)
.changeset/thick-bottles-push.md (1)
5-5: Clarify the changeset scope and fix description grammar.The description mentions a behavioral change for nl_335 (Natuurterreinen) that is not documented in the PR objectives, which focus on adding 2026 support and handling derogation logic. Additionally, the phrasing "is set to be not bouwland" is grammatically unclear.
Please clarify:
- Whether the nl_335 change is intentional and within scope for this PR, or if it should be moved to a separate changeset.
- Rephrase the description for clarity (e.g., "is now marked as non-bouwland" or similar).
fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts (2)
115-138: Bouwland detection logic and non‑bouwland codes (incl. nl_335) look correct.The time‑window check on
b_start/b_endand the non‑bouwland list (includingnl_335) match the comment and expected behavior: returnsfalsewhen there is no active cultivation or it’s in one of the explicit non‑bouwland codes, andtrueotherwise. This is clear and aligns with the earlier discussion around RVO codes.
254-259: Caching wrapper wiring looks correct and version‑safe.The memoized export uses the 2026‑specific function reference, a distinct cache key string, and
pkg.calculatorVersionfor invalidation. This matches the established pattern for other norms modules.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
.changeset/thick-bottles-push.md (1)
5-5: Improve clarity of the changelog entry for end users.The current note is highly technical and internal-facing. Consider rephrasing to make it more accessible to users:
Current: "Include at NL Stikstofgebruiksnormen that nl_335 (Natuurterreinen (incl. heide)) is set to be not bouwland"
Suggested: "Updated NL Stikstofgebruiksnormen classification for nl_335 (Natuurterreinen incl. heide) — now correctly classified as non-arable land"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
.changeset/thick-bottles-push.md(1 hunks)fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts(2 hunks)fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts(4 hunks)fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts(1 hunks)fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts
- fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts
- fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
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: 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.
📚 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/thick-bottles-push.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/thick-bottles-push.mdfdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts
📚 Learning: 2025-10-22T08:09:17.727Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 285
File: fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts:6-9
Timestamp: 2025-10-22T08:09:17.727Z
Learning: In fdm-calculator for NL 2025 phosphate stimulus ("Stimuleren organische stofrijke meststoffen"), the correct RVO mestcode groupings are: 25% discount group includes ["111", "112"] (Compost and Zeer schone compost), and the 75% discount group for organic farms includes ["40"] (Varkens - Vaste mest). The 75% base group includes ["110", "10", "61", "25", "56"].
Applied to files:
.changeset/thick-bottles-push.mdfdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.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:
.changeset/thick-bottles-push.mdfdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts
🧬 Code graph analysis (1)
fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (2)
fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts (1)
determineNLHoofdteelt(19-74)fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm-data.ts (1)
fosfaatNormsData(1-7)
🔇 Additional comments (6)
.changeset/thick-bottles-push.md (2)
1-3: Changeset format is valid.The YAML front matter and version bump type are correctly formatted. The minor bump is appropriate for adding the new 2026 support feature.
5-5: Consider whether the changelog entry captures all significant changes for this release.The changeset note documents a specific technical detail about
nl_335classification, but based on the PR scope (extensive 2026 support additions across norms calculations, UI routes, farm creation, and data structures), the changelog entry appears to capture only one detail among many substantial changes.Verify that this changeset entry comprehensively reflects the scope of the minor version bump, or ensure other changeset files document the broader 2026 support additions.
fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (4)
5-7: LGTM!Import refactoring correctly uses the generalized
determineNLHoofdteeltand centralizes type imports for 2025/2026 norms consolidation.
14-16: LGTM!Exporting
isCultivationGraslandenables reuse across 2025/2026 norms modules, avoiding code duplication.
146-147: LGTM!Correctly passes
2025to the generalizeddetermineNLHoofdteeltfunction, maintaining the expected date range logic for NL2025 norms while enabling code reuse with 2026. Based on learnings, this pattern of year-specific modules with parameterized helpers is appropriate.
153-157: LGTM!Data access pattern correctly updated to match the flat object structure in
fosfaatgebruiksnorm-data.ts. The added error handling for missing norms improves robustness and aligns with the updated JSDoc.
Summary by CodeRabbit
New Features
Bug Fixes
Tests
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.
Closes #350