Skip to content

Add 2026 year support with norms calculations#365

Merged
SvenVw merged 22 commits into
developmentfrom
FDM350
Dec 9, 2025
Merged

Add 2026 year support with norms calculations#365
SvenVw merged 22 commits into
developmentfrom
FDM350

Conversation

@SvenVw
Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw commented Dec 4, 2025

Summary by CodeRabbit

  • New Features

    • Full 2026 support: nitrogen & phosphate norms, fertilizer‑filling calculators, data tables and input collection; farm creation now offers next‑year selection and year‑aware flows.
    • Memoized/cached calculation endpoints for faster responses.
  • Bug Fixes

    • Korting edge case fixed for vanggewas sown Oct 15.
    • NL 335 (nature/heide) treated as non‑bouwland where applicable.
  • Tests

    • Expanded test coverage for 2025 and 2026 norms and filling logic.
  • Documentation

    • UI messages updated to show norms available for 2025 and 2026.

✏️ Tip: You can customize this high-level summary in your review settings.

Closes #350

@SvenVw SvenVw self-assigned this Dec 4, 2025
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Dec 4, 2025

🦋 Changeset detected

Latest commit: 990846b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@svenvw/fdm-app Minor
@svenvw/fdm-calculator Minor

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 4, 2025

Walkthrough

Adds 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

Cohort / File(s) Change Summary
Release metadata
.changeset/famous-hats-lick.md, .changeset/ready-geckos-notice.md, .changeset/silly-signs-ring.md, .changeset/small-icons-teach.md, .changeset/perky-heads-smash.md, .changeset/thick-bottles-push.md
New changeset files (minor/patch) declaring releases, 2026 availability, and a small bugfix note.
App routes & farm creation
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.$b_id.tsx, fdm-app/app/routes/farm.$b_id_farm.$calendar.norms._index.tsx, fdm-app/app/routes/farm.create._index.tsx
Allow calendar = "2026" (validation/messages updated); loader returns yearSelection used by farm creation UI; form defaults updated and years selection moved to loader data.
Calculator factory & exports
fdm-calculator/src/norms/index.ts, fdm-calculator/src/norms/index.test.ts, fdm-calculator/src/index.ts, fdm-calculator/src/norms/farm.ts
Generalize factories to Years = "2025"
NL shared types centralization
fdm-calculator/src/norms/nl/types.ts, fdm-calculator/src/norms/nl/2025/.../types.d.ts
Introduce centralized NL result and filling types; remove duplicated 2025-specific exported types and update imports across modules.
2025 refactors / helper param
fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts, .../hoofdteelt.test.ts, .../stikstofgebruiksnorm.ts, .../fosfaatgebruiksnorm.ts
Rename determineNL2025Hoofdteelt → determineNLHoofdteelt(year) and update call sites; parameterize date bounds; update imports to centralized types.
2025 filling — collector rename & signature
fdm-calculator/src/norms/nl/2025/filling/input.ts, .../input.test.ts, .../filling/*.ts
Rename collectInputForFertilizerApplicationFilling → collectNL2025InputForFertilizerApplicationFilling and add fosfaatgebruiksnorm parameter; update tests and type import paths.
2025 fosfaat data shape
fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm-data.ts
Changed fosfaatNormsData from single-element array to plain object; access patterns adjusted.
2026 value — input, types, data
fdm-calculator/src/norms/nl/2026/value/input.ts, .../input.test.ts, .../types.d.ts, .../fosfaatgebruiksnorm-data.ts, .../stikstofgebruiksnorm-data.ts
New NL2026 input collectors, type declarations, and static data for 2026 norms (fosfaat and nitrogen standards).
2026 value — calculators & tests
fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts, .../fosfaatgebruiksnorm.ts, .../dierlijke-mest-gebruiksnorm.ts, plus tests
Implement NL2026 calculators (stikstof, fosfaat, dierlijke-mest) with memoized getters and add unit tests.
2026 filling — input, types, tables
fdm-calculator/src/norms/nl/2026/filling/input.ts, .../input.test.ts, .../types.d.ts, .../table-9.ts, .../table-11-mestcodes.ts
Add NL2026 fertilizer-filling input collector, typed input shapes, and static tables (table9, table11Mestcodes) used by filling calculators.
2026 filling — calculators & tests
fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts, .../fosfaatgebruiksnorm.ts, .../dierlijke-mest-gebruiksnorm.ts, plus tests
Implement NL2026 fertilizer-filling calculators (nitrogen, phosphate with discounting, manure), produce per-application breakdowns, add memoized getters and extensive tests.
Tests — index & broad coverage
fdm-calculator/src/norms/index.test.ts, many fdm-calculator/src/norms/nl/2026/**/*.test.ts
Extend tests to cover 2026 exports, collectors, value/filling calculators, and adjust 2025 test imports/names.

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
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

  • Areas to focus on:
    • 2026 filling calculators (fdm-calculator/src/norms/nl/2026/filling/*.ts) — complex domain rules, discounting, table lookups, edge cases.
    • 2026 value calculators and selection logic (fdm-calculator/src/norms/nl/2026/value/*.ts) — standards selection, varieties/subtypes, korting logic.
    • Public API/type surface changes: centralization in fdm-calculator/src/norms/nl/types.ts and removed 2025-specific declarations.
    • Signature changes and renamed functions (determineNLHoofdteelt(year), collectNL2025/collectNL2026) — ensure all call sites/tests updated.
    • Memoization wrapper usage (withCalculationCache) — cache key/version correctness.
    • fdm-app loaders and yearSelection integration — ensure no regressions in forms/routing.

Possibly related PRs

Suggested reviewers

  • BoraIneviNMI
  • gerardhros

Poem

🐇 I hopped from twenty‑twenty‑five to twenty‑twenty‑six,

Tables, tests and caches tucked in my mix.
Calculators hum, loaders hand years with care,
Fields find their norms — per app, per share.
🥕 Hooray — a rabbit’s tiny, diligent cheer!

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'Add 2026 year support with norms calculations' directly describes the primary change: enabling 2026 in the application and implementing 2026 norms calculations.
Linked Issues check ✅ Passed All requirements from issue #350 are substantially addressed: fdm-calculator has 2026 norms directory structure with year-generic helper functions; fdm-app calendar routes accept 2026; integration uses dynamic year context; farm creation allows 2026; atlas and derogation constraints remain unchanged.
Out of Scope Changes check ✅ Passed All changes are within scope: changesets document releases; type reorganization in fdm-calculator aligns with 2026 modularization; 2025 fosfaat data structure change supports the new structure used in 2026; stikstof 2025 logic tweak (nl_335) is a valid refinement.
Docstring Coverage ✅ Passed Docstring coverage is 86.67% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch FDM350

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Dec 4, 2025

Codecov Report

❌ Patch coverage is 84.52138% with 76 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.23%. Comparing base (8baece4) to head (990846b).
⚠️ Report is 23 commits behind head on development.

Files with missing lines Patch % Lines
...tor/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts 41.79% 39 Missing ⚠️
...or/src/norms/nl/2026/value/stikstofgebruiksnorm.ts 80.23% 33 Missing ⚠️
fdm-calculator/src/norms/nl/2026/value/input.ts 88.88% 2 Missing ⚠️
...r/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts 98.82% 1 Missing ⚠️
.../src/norms/nl/2026/filling/stikstofgebruiksnorm.ts 98.79% 1 Missing ⚠️
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     
Flag Coverage Δ
fdm-calculator 87.01% <84.52%> (-0.81%) ⬇️
fdm-core 87.08% <ø> (ø)
fdm-data 92.12% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot changed the title @coderabbitai Add 2026 year support with norms calculations Dec 4, 2025
@coderabbitai coderabbitai Bot added branch:development Issue only affecting development, not the main branch (yet) fdm-app fdm-calculator fdm-core fdm-docs labels Dec 4, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Select component uses defaultValue={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_year default 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 fosfaatNormsData is 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 analyses
fdm-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} input but calculateNL2026DierlijkeMestGebruiksNorm takes 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: string
fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts (1)

213-220: Redundant day check in January condition.

The condition appDay <= 31 is 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

📥 Commits

Reviewing files that changed from the base of the PR and between 752a623 and b4ff19a.

⛔ Files ignored due to path filters (2)
  • fdm-app/app/lib/calendar.ts is excluded by !fdm-app/app/lib/**
  • pnpm-lock.yaml is 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.md
  • fdm-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.md
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.ts
  • fdm-calculator/src/norms/nl/2026/value/input.ts
  • .changeset/silly-signs-ring.md
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
  • fdm-calculator/src/index.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm-data.ts
  • fdm-calculator/src/norms/nl/2026/value/input.test.ts
  • fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2025/filling/input.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.ts
  • fdm-calculator/src/norms/nl/2026/value/input.ts
  • fdm-calculator/src/norms/nl/2026/filling/table-11-mestcodes.ts
  • fdm-calculator/src/norms/nl/2026/value/types.d.ts
  • fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.test.ts
  • fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm-data.ts
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.norms._index.tsx
  • fdm-calculator/src/norms/nl/types.ts
  • .changeset/silly-signs-ring.md
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.$b_id.tsx
  • fdm-calculator/src/norms/nl/2025/filling/input.test.ts
  • fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/types.d.ts
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts
  • fdm-calculator/src/norms/nl/2026/filling/table-9.ts
  • fdm-calculator/src/index.ts
  • fdm-calculator/src/norms/nl/2025/value/hoofdteelt.test.ts
  • fdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/index.ts
  • fdm-calculator/src/norms/index.test.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm-data.ts
  • fdm-calculator/src/norms/nl/2026/value/input.test.ts
  • fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2025/filling/input.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.ts
  • fdm-calculator/src/norms/nl/2026/value/input.ts
  • fdm-calculator/src/norms/nl/2026/filling/table-11-mestcodes.ts
  • fdm-calculator/src/norms/nl/2026/value/types.d.ts
  • fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.test.ts
  • fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm-data.ts
  • fdm-calculator/src/norms/nl/types.ts
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.$b_id.tsx
  • fdm-calculator/src/norms/nl/2025/filling/input.test.ts
  • fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2026/filling/types.d.ts
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/table-9.ts
  • fdm-calculator/src/index.ts
  • fdm-calculator/src/norms/nl/2025/value/hoofdteelt.test.ts
  • fdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/index.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.test.ts
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2025/filling/input.test.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.test.ts
  • fdm-calculator/src/norms/nl/2025/filling/input.test.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.test.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2025/filling/input.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.ts
  • fdm-calculator/src/norms/nl/2026/value/input.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.test.ts
  • fdm-calculator/src/norms/nl/2025/filling/input.test.ts
  • fdm-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.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.test.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.ts
  • fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.test.ts
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2025/filling/input.test.ts
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/filling/input.ts
  • fdm-calculator/src/norms/nl/2026/value/input.ts
  • fdm-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.ts
  • fdm-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.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.test.ts
  • .changeset/silly-signs-ring.md
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts
  • fdm-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.md
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2026/filling/types.d.ts
  • fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/table-9.ts
  • fdm-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.tsx
  • fdm-app/app/routes/farm.create._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.$b_id.tsx
  • fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts
  • fdm-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.tsx
  • fdm-app/app/routes/farm.create._index.tsx
  • fdm-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.tsx
  • 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-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.md
  • fdm-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

Comment thread fdm-calculator/src/norms/index.test.ts Outdated
Comment thread fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts Outdated
Comment thread fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts Outdated
Comment thread fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts Outdated
Comment thread fdm-calculator/src/norms/nl/2026/filling/input.test.ts
Comment thread fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
Comment thread fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts
Comment thread fdm-calculator/src/norms/nl/2026/value/input.test.ts Outdated
Comment thread fdm-calculator/src/norms/nl/2026/value/input.test.ts Outdated
Comment thread fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts
@SvenVw
Copy link
Copy Markdown
Collaborator Author

SvenVw commented Dec 5, 2025

Tests are failing due to receiving a 500 when pulling node:24-alpine image

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
fdm-calculator/src/norms/nl/2025/value/hoofdteelt.ts (1)

16-16: JSDoc example still shows unnecessary await.

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 isDerogationGrantedForYear at line 18 is not used because collectNL2026InputForNorms does not call this function. The implementation only calls getField, getGrazingIntention, getCultivations, and getCurrentSoilData.

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 of vi.mock() for better reliability.

Based on learnings, the team prefers to avoid vi mocking 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, while organicRichApplications already tracks originalIndex. 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 calculateNL2026StikstofGebruiksNorm in 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

📥 Commits

Reviewing files that changed from the base of the PR and between b4ff19a and e933a1c.

📒 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.ts
  • fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/value/input.test.ts
  • fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/value/input.test.ts
  • fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2026/filling/fosfaatgebruiksnorm.ts
  • fdm-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.ts
  • fdm-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.ts
  • fdm-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 NL2026NormsInput type
  • All function calls occur with the correct arguments and timeframes
  • Soil analysis data is correctly transformed (picking a_p_cc and a_p_al parameters)
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 Decimal for precise arithmetic
  • Only counts applications where p_type_nitratesdirective is true (removing derogation logic per PR objectives)
  • Falls back to RVO table values when fertilizer-specific p_n_rt is missing

Based 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-error for 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 getNormsForCultivation function 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 determineSubTypeOmschrijving function 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 Decimal for 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 passing cultivation.b_lu_end to getNormsForCultivation at line 1703.

The database schema for cultivation_ending.b_lu_end lacks a .notNull() constraint, making it nullable. This means cultivation.b_lu_end from NL2026NormsInputForCultivation can be Date | null, but getNormsForCultivation (line 1246) expects a Date parameter. The null check at line 311 in calculateKorting confirms the codebase already recognizes that b_lu_end can be null. Verify that the cultivation selected at line 1701 always has a non-null b_lu_end, or add a defensive null check before calling getNormsForCultivation.

⛔ 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.

Comment thread fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts Outdated
Comment thread fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 returning null.

The JSDoc at line 183 states "Returns null if 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 null if 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 returning null.

The JSDoc at line 106 states "Returns null if a norm cannot be determined" but the function signature is Promise<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 shared getFosfaatKlasse logic.

The getFosfaatKlasse function 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 ../../utils or ../../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 year parameter in the map callback shadows the year from 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 loop

The overall structure of calculateNL2026FertilizerApplicationFillingForStikstofGebruiksNorm looks solid, but a couple of details are worth revisiting:

  1. Nitrogen content fallback (Lines 49–61)

    • Treating p_n_rt === 0 as “unknown” and silently defaulting to 0 when Table 11 has no p_n_rt may hide catalogue/config mistakes and understate N usage.
    • If the intention is that every relevant p_type_rvo must 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_rt
    
  •    if (
    
  •        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_rt
    
  •    if (
    
  •        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.
    
    
  1. onFarmProduced heuristic (Lines 66–77)

    • const onFarmProduced = has_grazing_intention currently marks all fertilizers as on‑farm produced whenever the farm has grazing, regardless of p_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_rvo codes, or wiring it to a dedicated fertilizer property when that becomes available.
  2. Formula comment vs implementation (Lines 79–85, 91)

    • The comment says amount * nitrogen content * (working coefficient / 100) / 1000, but the code multiplies directly by p_n_wcl and formats it as ${p_n_wcl * 100}%, which implies p_n_wcl is 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: Improve getWorkingCoefficient fallback behavior and remove unused variable

Two small points in getWorkingCoefficient:

  1. Fallback when subtypes don’t match (Lines 170–239)

    • Right now, if an entry has subTypes but none matches, and that entry also has a top‑level p_n_wcl, that top‑level value is never used because of the else if:
    if (entry.subTypes) {
      // ...
    } else if (entry.p_n_wcl !== undefined) {
      return { p_n_wcl: entry.p_n_wcl, ... }
    }

    If table9 ever contains entries with both a general p_n_wcl and 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_wcl after 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_wcl
    
  •            return {
    
  •                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.
    
    
  1. Unused appDay variable (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

📥 Commits

Reviewing files that changed from the base of the PR and between e933a1c and 1673a6e.

📒 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.ts
  • fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm-data.ts
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
  • fdm-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.ts
  • 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/value/fosfaatgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts
  • fdm-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 withCalculationCache with 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 normValue and normSource
  • Error messages appropriately reference "NL 2026"

160-176: LGTM!

The memoized wrapper is correctly configured with proper JSDoc (including @throws documentation 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 determineNL2025Hoofdteelt to the year-parameterized determineNLHoofdteelt.


14-22: Good: Exporting isCultivationGrasland enables reuse.

Adding export to 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! Using field.value correctly reflects the form's controlled state instead of a hardcoded value.


54-54: Remove import of non-existent function getCalendarSelection.

The import statement on line 54 references getCalendarSelection from ../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 the getCalendarSelection function in app/lib/calendar.ts or 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: isBouwland logic looks correct and straightforward

The date‑range selection and exclusion of the specified non‑bouwland LU codes are clear and consistent. Inclusive start/end checks on b_start/b_end also make sense for determining the active cultivation at p_app_date.


255-260: Cached wrapper wiring looks consistent

The memoized getNL2026FertilizerApplicationFillingForStikstofGebruiksNorm correctly wraps the async calculator with withCalculationCache and uses a stable cache key plus pkg.calculatorVersion. This aligns with the existing pattern and should be good to go.

Comment thread fdm-calculator/src/norms/nl/2026/value/dierlijke-mest-gebruiksnorm.ts Outdated
@SvenVw SvenVw requested a review from gerardhros December 5, 2025 09:30
Comment thread fdm-calculator/src/norms/nl/2026/filling/input.test.ts
Comment thread fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
Comment thread fdm-calculator/src/norms/nl/2026/filling/types.d.ts
Comment thread fdm-calculator/src/norms/nl/2026/value/fosfaatgebruiksnorm.ts
Comment thread fdm-calculator/src/norms/nl/2026/value/input.ts
Comment thread fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts
Copy link
Copy Markdown
Collaborator

@gerardhros gerardhros left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 for isCultivationGrasland (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_intention as 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 onFarmProduced status 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 <= 2025 to identify previous year plantings for 2026 calculations, and line 222 correctly passes year 2026 to determineNLHoofdteelt.

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 sync

Switching to determineNLHoofdteelt and importing GebruiksnormResult from the shared ../../types aligns this module with the generalized NL norms helpers; the types and usage here look consistent.

Minor doc nit: the JSDoc for calculateNL2025StikstofGebruiksNorm still documents a separate kortingDescription field on the result, but the function (and the shared GebruiksnormResult type) only surface normValue and normSource, with the korting text composed into normSource. 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 parameterized

The change to derive hoofdteelt2025 via determineNLHoofdteelt on the filtered 2025 cultivations, with the explicit 2025 year 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 pass currentYear instead of the literal 2025 to avoid duplicating the value, but it’s not required.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1673a6e and 66d814e.

📒 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.ts
  • fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts
  • fdm-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.ts
  • fdm-calculator/src/norms/nl/2026/value/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts
  • fdm-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.ts
  • fdm-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.ts
  • fdm-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.ts
  • fdm-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 determineNLHoofdteelt and passing 2025 explicitly, plus switching to fosfaatNormsData[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 the fosfaatgebruiksnorm-data.ts object shape and, combined with the explicit error on missing normsForKlasse, 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 (@throws for missing soil data or norms, and reference to fosfaatgebruiksnorm-data.ts for 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 withCalculationCache to 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, and isFieldInNVGebied are 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:

  1. Determines hoofdteelt and handles the special braak case (lines 444-458)
  2. Retrieves geographical context (NV gebied, region) (lines 466-467)
  3. Matches and refines nitrogen standards with validation (lines 470-501)
  4. Determines sub-type and retrieves applicable norms (lines 504-521)
  5. 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 withCalculationCache with 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 consistent

Using 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 logic

The updated comment (“between July 15th, 2024 and February 1st 2025 (exclusive)”) now correctly reflects previousYear = 2024 and currentYear = 2025 used in the surrounding date checks, resolving the earlier year mismatch.


441-448: Korting descriptions now align with the sowing date conditions

The textual ranges (“uiterlijk 1 oktober”, “2 t/m 14 oktober”, “15 t/m 31 oktober”) now accurately describe the corresponding sowDate comparisons against october1, october15, and november1, which reduces ambiguity for downstream users/debugging.


544-544: Main hoofdteelt determination correctly uses generalized helper with 2025

Calling determineNLHoofdteelt(cultivations, 2025) at the start of calculateNL2025StikstofGebruiksNorm keeps the hoofdteelt logic centralized in hoofdteelt.ts while 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.

Comment thread fdm-calculator/src/norms/nl/2026/filling/stikstofgebruiksnorm.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 NormFilling all look consistent and correct for 2026.

Two small, optional improvements:

  • When both fertilizer.p_n_rt and the table‑11 entry are missing, you silently fall back to 0. 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 * 100 in 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 getRegion from 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 future applicationPeriod values.

The matching sequence (p_type_rvo → optional onFarmProduced → subType filters on grazing, soil, bouwland, and application window) is clear and should behave correctly with the current table9 data.

One maintainability edge case: subType.applicationPeriod is only interpreted when it equals "1 september t/m 31 januari". Any future additional applicationPeriod strings in table9 will 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 table9 as structured month/day ranges instead of Dutch strings, or
  • at least guard unknown applicationPeriod values (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

📥 Commits

Reviewing files that changed from the base of the PR and between 66d814e and 8f605f4.

📒 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.md
  • 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:

  • .changeset/thick-bottles-push.md
  • fdm-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.md
  • fdm-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:

  1. Whether the nl_335 change is intentional and within scope for this PR, or if it should be moved to a separate changeset.
  2. 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_end and the non‑bouwland list (including nl_335) match the comment and expected behavior: returns false when there is no active cultivation or it’s in one of the explicit non‑bouwland codes, and true otherwise. 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.calculatorVersion for invalidation. This matches the established pattern for other norms modules.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 66d814e and 990846b.

📒 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.md
  • fdm-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.md
  • fdm-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.md
  • fdm-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_335 classification, 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 determineNLHoofdteelt and centralizes type imports for 2025/2026 norms consolidation.


14-16: LGTM!

Exporting isCultivationGrasland enables reuse across 2025/2026 norms modules, avoiding code duplication.


146-147: LGTM!

Correctly passes 2025 to the generalized determineNLHoofdteelt function, 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.

@SvenVw SvenVw merged commit bbaa9e9 into development Dec 9, 2025
8 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

branch:development Issue only affecting development, not the main branch (yet) fdm-app fdm-calculator fdm-core fdm-docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enable 2026 in fdm-app

2 participants