Skip to content

Add 2025 Dutch fertilizer norm calculations with grazing and organic#285

Merged
SvenVw merged 93 commits into
developmentfrom
FDM284
Oct 31, 2025
Merged

Add 2025 Dutch fertilizer norm calculations with grazing and organic#285
SvenVw merged 93 commits into
developmentfrom
FDM284

Conversation

@SvenVw
Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw commented Sep 30, 2025

Summary by CodeRabbit

  • New Features

    • Manage organic certifications (TRACES/SKAL with dates) and per‑year grazing intention in Farm settings.
    • Field and farm "Gebruiksruimte" pages: norms, fillings, per‑application breakdowns, cards, progress bars and tooltips; field selector in header.
    • RVO "Mestcode" integrated across fertilizer UI: input, display, color‑coded badges with hover-to-view full labels.
  • Bug Fixes

    • Fixed breadcrumb link in fertilizers header.
    • Nitrogen norms now differentiate grazing vs. mowing; fertilizer table coloring extended to RVO mestcodes.
  • Documentation

    • Added/updated 2025 norms docs and notes reflecting Mestcode (RVO) usage.

Closes #284

…ationValid, `listOrganicCertifications`, `getOrganicCertification` and `removeOrganicCertification` to manage organic certifications of a farm
@SvenVw SvenVw self-assigned this Sep 30, 2025
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Sep 30, 2025

🦋 Changeset detected

Latest commit: 554ec44

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

This PR includes changesets to release 5 packages
Name Type
@svenvw/fdm-app Minor
@svenvw/fdm-core Minor
@svenvw/fdm-calculator Minor
@svenvw/fdm-data Minor
@svenvw/fdm-docs 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 Sep 30, 2025

Walkthrough

Adds RVO mestcode support and NL2025 filling calculations: introduces p_type_rvo across schema, core, data and app; adds organic certification and grazing intention DB/API/UI; implements per‑application and aggregated N/P/manure filling calculators, new norms UI/pages/components, table‑9/11 data, and tests/changesets.

Changes

Cohort / File(s) Summary
Changesets
.changeset/*
Many new changeset files adding version bumps and release notes across packages (@svenvw/fdm-app, @svenvw/fdm-core, @svenvw/fdm-calculator, @svenvw/fdm-data).
DB schema & cascade
fdm-core/src/db/schema.ts, fdm-core/src/farm.ts
Add p_type_rvo enum/options; new tables organic_certifications, organic_certifications_holding, intending_grazing; extend removeFarm to cascade-delete related derogations, organic certifications and grazing intentions.
Core — fertilizer API & params
fdm-core/src/fertilizer.{ts,d.ts,test.ts}
Propagate p_type_rvo through add/update/get catalogue flows; derive p_type from p_type_rvo when present; update parameter descriptions to use p_type_rvo; add mapping helper and tests.
Core — organic & grazing
fdm-core/src/organic.*, fdm-core/src/organic.d.ts, fdm-core/src/grazing_intention.*, fdm-core/src/index.ts
New organic certification and grazing-intention modules with CRUD, validation, tests, types, and public re-exports (add/list/get/remove/isValid*, set/get/remove grazing intentions).
Calculator — NL2025 norms & filling
fdm-calculator/src/index.ts, fdm-calculator/src/norms/*, fdm-calculator/src/norms/nl/2025/filling/*
Add input collection for fertilizer‑application fillings, concrete per-application and aggregated filling calculations (nitrogen, phosphate with organic-rich discounting, manure), table‑9/table‑11 data, new types (NormFilling, NL2025 inputs), createFunctionsForFertilizerApplicationFilling, and extensive tests.
Calculator — value modules & tests
fdm-calculator/src/norms/nl/2025/value/*
Rename value functions to calculate*; introduce has_grazing_intention in inputs; restructure stikstof data into sub_types (beweiden/volledig maaien); update tests for grazing/subtype logic.
App — fertilizer UI & routes
fdm-app/app/components/blocks/fertilizer/*, fdm-app/app/routes/**/fertilizers*.tsx, fdm-app/app/components/blocks/organic-certification/schema.ts, fdm-app/package.json
Switch forms/columns/defaults from p_type to p_type_rvo; add p_type_rvo_label; unify badge rendering with optional tooltip; route loaders/actions include p_type_rvo and set p_type null on submit; add client Zod organic-cert schema and small UI deps.
App — norms UI & routes
fdm-app/app/components/blocks/norms/*, fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.*, fdm-app/app/routes/farm.$b_id_farm.$calendar.norms._index.tsx
New field- and farm-level norms pages and components (NormCard, FieldNormsContent, FertilizerApplicationCard); farm-level aggregation uses AggregatedNormFillingsToFarmLevel; header field selector added; loader wiring to calculator functions.
Data — BAAT catalogue
fdm-data/src/fertilizers/catalogues/baat.*, fdm-data/src/fertilizers/d.ts
BAAT entries augmented with p_type_rvo; catalogue mapping and hashing/tests updated to include the new field.
Tests & misc
fdm-calculator/.gitignore, many *.test.ts across packages
Add coverage ignore and broad unit tests across calculator/core/data/app validating new behaviors, edge cases, and public API shape changes.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App as fdm-app
    participant Core as fdm-core
    participant Calc as fdm-calculator
    participant DB as Database

    rect rgba(223,242,255,0.9)
    Note over User,App: Add organic certification
    User->>App: submit certification form
    App->>App: client Zod validate
    App->>Core: addOrganicCertification(payload)
    Core->>DB: insert organic + holding (tx)
    DB-->>Core: OK
    Core-->>App: success
    App-->>User: toast
    end

    rect rgba(233,255,240,0.9)
    Note over User,App: Toggle grazing intention (year)
    User->>App: toggle switch
    App->>Core: setGrazingIntention(year, state)
    Core->>DB: upsert intending_grazing
    DB-->>Core: OK
    Core-->>App: success
    App-->>User: toast
    end

    rect rgba(255,248,225,0.9)
    Note over User,Calc: Render field norms (NL2025)
    User->>App: open field norms page
    App->>Core: getField(), getFertilizerApplications(), getFertilizers()
    Core-->>App: field + apps + fertilizers
    App->>Calc: collectInputForFertilizerApplicationFilling(params)
    Calc->>Core: getGrazingIntention(), isOrganicCertificationValid()
    Core-->>Calc: grazing & organic states
    Calc->>Calc: calculate per-app N/P/manure fillings
    Calc-->>App: NormFilling + per-app breakdown
    App->>User: render NormCard(s) and application cards
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas needing extra attention:

  • NL2025 filling algorithms (fosfaat/stikstof/dierlijke‑mest): Decimal arithmetic, discounting strategy, application ordering and edge cases.
  • table‑9/table‑11 data correctness and mappings between p_type_rvo and nutrient values.
  • DB schema additions and removeFarm cascade behavior (migrations, referential integrity).
  • Cross‑package API surface: p_type_rvo propagation and has_grazing_intention (fdm-core ↔ fdm-calculator ↔ fdm-app).
  • New UI routes/actions (organic-certification, grazing-intention) and form validation wiring.

Possibly related issues

Possibly related PRs

Suggested reviewers

  • gerardhros

Poem

"🐰 I hopped through code and fields so wide,
RVO codes tucked safe inside,
Grazing toggles, certs held near,
Norms counted true, per-application clear,
Hooray — the meadow's data's right!"

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 2025 Dutch fertilizer norm calculations with grazing and organic" clearly and concisely summarizes the primary changes in the changeset. It accurately reflects the main objectives: implementing 2025 Dutch fertilizer norm calculations while adding support for grazing and organic farm status tracking. The title is specific enough to convey the purpose without excessive noise or vague terminology, and it aligns with the substantial code additions across multiple packages (fdm-calculator, fdm-core, fdm-app) to enable these features.
Linked Issues Check ✅ Passed The pull request implements the primary coding requirements from issue #284. The data model changes include new year-scoped tables for storing grazing intentions (intending_grazing) and organic certifications (organic_certifications and organic_certifications_holding) mirroring the derogations pattern; the fertilizers_catalogue is extended with the p_type_rvo field. fdm-core includes new modules (grazing_intention.ts, organic.ts) with functions to manage yearly properties, and fertilizer operations (add/update/get) are updated to handle p_type_rvo. fdm-calculator implements phosphate stimulus classification with 75% and 25% categories based on RVO codes and farm organic status, plus differentiated phosphate counting rules including minimum application requirements. fdm-app adds year-scoped toggles for grazing and organic status in farm settings, integrates the RVO Mestcode field in fertilizer management, and updates norms pages to display calculation results. The implementation comprehensively addresses the stated objectives.
Out of Scope Changes Check ✅ Passed The changeset is tightly focused on the 2025 fertilizer norm calculation feature and its related infrastructure. All significant changes align with the linked issue objectives: schema extensions, new core modules, calculator implementations, and UI updates. Minor changes include documentation updates (appropriately adding 2025 norms documentation and reorganizing the docs structure), metadata files (changesets, blog tags), and a small header link fix. These ancillary changes support or document the primary feature without introducing unrelated functionality. No out-of-scope code changes that contradict or diverge from the stated objectives are present.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch FDM284

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 Sep 30, 2025

Codecov Report

❌ Patch coverage is 95.13151% with 87 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.35%. Comparing base (afd5e99) to head (554ec44).
⚠️ Report is 94 commits behind head on development.

Files with missing lines Patch % Lines
fdm-core/src/organic.ts 88.17% 35 Missing ⚠️
fdm-core/src/farm.ts 38.09% 26 Missing ⚠️
fdm-core/src/fertilizer.ts 79.41% 21 Missing ⚠️
...r/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts 98.96% 2 Missing ⚠️
fdm-core/src/index.ts 0.00% 2 Missing ⚠️
.../src/norms/nl/2025/filling/stikstofgebruiksnorm.ts 99.40% 1 Missing ⚠️
Additional details and impacted files
@@               Coverage Diff               @@
##           development     #285      +/-   ##
===============================================
+ Coverage        93.11%   93.35%   +0.24%     
===============================================
  Files               81       91      +10     
  Lines            13322    15028    +1706     
  Branches          1361     1523     +162     
===============================================
+ Hits             12405    14030    +1625     
- Misses             915      996      +81     
  Partials             2        2              
Flag Coverage Δ
fdm-calculator 95.43% <99.72%> (+0.81%) ⬆️
fdm-core 91.77% <88.23%> (-0.28%) ⬇️
fdm-data 94.04% <100.00%> (+0.01%) ⬆️

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.

@SvenVw SvenVw requested a review from gerardhros October 22, 2025 09:28
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 (5)
fdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.ts (1)

26-28: Robust centroid handling (tuple vs GeoJSON Point).

Field["b_centroid"] appears as a tuple here, but other tests use GeoJSON Points. Normalize input to accept both to avoid runtime errors.

Apply:

+function getLonLat(b_centroid: Field["b_centroid"]): [number, number] {
+  if (Array.isArray(b_centroid)) return [b_centroid[0], b_centroid[1]]
+  // GeoJSON Point
+  if (b_centroid && (b_centroid as any).type === "Point" && Array.isArray((b_centroid as any).coordinates)) {
+    const [lon, lat] = (b_centroid as any).coordinates
+    return [lon, lat]
+  }
+  throw new Error("Unsupported b_centroid shape")
+}
-const longitude = b_centroid[0]
-const latitude = b_centroid[1]
+const [longitude, latitude] = getLonLat(b_centroid)

Repeat for the other functions in this file.

Also applies to: 61-63, 96-102

fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (1)

145-149: Input validation rejects valid zero values.

0 is a valid soil metric. Current falsy check throws incorrectly.

Apply:

-    if (!a_p_al || !a_p_cc) {
+    if (a_p_al == null || a_p_cc == null || !Number.isFinite(a_p_al) || !Number.isFinite(a_p_cc)) {
         throw new Error(
             "Missing soil analysis data for NL 2025 Fosfaatgebruiksnorm",
         )
     }
fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.test.ts (1)

8-12: <!-- [scratchpad_update]
[task verification_1/1]
Web search did not provide codebase-specific information. Need to inspect the actual implementation files.

[pending]

  • Find fosfaatgebruiksnorm value function implementation
  • Check if GeoJSON to tuple conversion occurs
  • Determine if type mismatch is actual error or uses type assertions
    [scratchpad_end] --><!-- [scratchpad_update]
    [task verification_2/2]
    Web search confirms GeoJSON Point coordinates are Position (number[]) according to RFC7946.
    Still need to inspect the actual implementation to check for normalization or type assertions.

[pending]

  • Search codebase for fosfaatgebruiksnorm value function
  • Determine if GeoJSON object is accepted or type-asserted
  • Check if function normalizes to tuple

[actions]

  • Run script to find fosfaatgebruiksnorm.ts implementation
  • Check function signature and how it handles b_centroid parameter
    [scratchpad_end] --><!-- [scratchpad_update]
    [task verification_2/2]
    Web search did not return the specific repository. Need to search directly in codebase using shell scripts.

[pending]

  • Search fosfaatgebruiksnorm.ts implementation
  • Check function signature and input typing
  • Determine how b_centroid is consumed in the value function

[actions]

  • Use fd/rg to find fosfaatgebruiksnorm value function implementation
  • Inspect it to see if it handles GeoJSON or tuple shape
    [scratchpad_end] --><!-- [scratchpad_update]
    [task verification_3/3]
    Web search confirms that "as Field" type assertion is used in test at line 44 of stikstofgebruiksnorm.test.ts (and elsewhere).

Key findings:

  • Type assertions bypass TypeScript's strictness and are compile-time only
  • They do NOT guarantee type safety
  • Both directions of assignability are checked: value to asserted type AND asserted type to value
  • Type assertion prevents compiler errors even when shapes don't match

Critical observation:

  • In fosfaatgebruiksnorm.test.ts lines 11, 30: b_centroid: { type: "Point", coordinates: [5.0, 52.0] }
    This is a GeoJSON Point object, NOT a [number, number] tuple
  • fdm-core/src/field.d.ts line 9: b_centroid: [number, number] (type definition)
  • Other test files using stikstofgebruiksnorm.test.ts all use tuple format: b_centroid: [5.6279889, 51.975571]

The issue is clear:

  • fosfaatgebruiksnorm.test.ts uses GeoJSON Point format which violates the Field type definition
  • The type definition expects [number, number]
  • This inconsistency could cause runtime errors if the value function tries to access coordinates directly as an array

The review comment is CORRECT. The GeoJSON Point shape differs from the defined [number, number] tuple shape.

Web searches have been exhausted. Based on the data, the review comment is correct and needs to be confirmed.
[scratchpad_end] -->

Based on the verification results, the review comment is accurate. The script output clearly shows an inconsistency:

Defined type (fdm-core/src/field.d.ts:9): b_centroid: [number, number] (tuple)

Usage in fosfaatgebruiksnorm.test.ts (lines 11, 30): b_centroid: { type: "Point", coordinates: [5.0, 52.0] } (GeoJSON Point object)

Consistent usage elsewhere: All other tests (stikstofgebruiksnorm.test.ts, dierlijke-mest-gebruiksnorm.test.ts, etc.) use the tuple format [lon, lat].

The GeoJSON Point object shape is incompatible with the declared [number, number] tuple type. While test files with as Field type assertions may suppress TypeScript errors, this creates a hidden type mismatch that could cause runtime issues if the value functions attempt to access b_centroid as a numeric tuple.

Fix b_centroid shape in fosfaatgebruiksnorm.test.ts (lines 11 and 30).

Change b_centroid: { type: "Point", coordinates: [5.0, 52.0] } to b_centroid: [5.0, 52.0] to align with the Field type definition and other tests.

fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (1)

390-394: Vanggewas sowing window off-by-one excludes July 15.

The filter uses ">" for July 15, 2024; the comment states "between July 15th, 2024 and January 31st, 2025". Sowings on July 15 should qualify but are currently excluded.

Apply:

-        const matchingYear =
-            prevCultivation.b_lu_start.getTime() <
-                new Date(currentYear, 1, 1).getTime() &&
-            prevCultivation.b_lu_start.getTime() >
-                new Date(previousYear, 6, 15).getTime() // Vanggewas should be sown between July 15th, 2024 and January 31th 2025
+        const matchingYear =
+            prevCultivation.b_lu_start.getTime() <
+                new Date(currentYear, 1, 1).getTime() && // < Feb 1, 2025
+            prevCultivation.b_lu_start.getTime() >=
+                new Date(previousYear, 6, 15).getTime() // >= Jul 15, 2024 (inclusive)
fdm-calculator/src/norms/index.test.ts (1)

37-41: Trim the unsupported-year test input.

Test currently uses " 2024" (with a leading space). Pass "2024" to better reflect the intent and avoid masking whitespace bugs.

-        expect(() => createFunctionsForNorms("NL", " 2024")).toThrow(
+        expect(() => createFunctionsForNorms("NL", "2024")).toThrow(
             "Year not supported",
         )
♻️ Duplicate comments (1)
fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts (1)

67-81: On‑farm detection heuristic is known issue (tracked).

Heuristic p_type_rvo ⇒ onFarmProduced is inaccurate; use fertilizer.p_origin when available. Leaving as-is since it’s tracked in a separate issue.

🧹 Nitpick comments (13)
fdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.ts (2)

21-23: Typo in function name: GWBG vs GWGB.

Rename to isFieldInGWBGGebied for correctness and consistency with the acronym.

Apply:

-export async function isFieldInGWGBGebied(
+export async function isFieldInGWBGGebied(
     b_centroid: Field["b_centroid"],
 ): Promise<boolean> {

And update its call sites in this file.


112-114: Error string polish.

Fix double space and wording.

Apply:

-`Unknown  derogatieVrijeZoneCodes code: ${derogatieVrijeZoneCode} for coordinates ${longitude}, ${latitude}`,
+`Unknown derogatie-vrije zone code: ${derogatieVrijeZoneCode} for coordinates ${longitude}, ${latitude}`,
fdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.test.ts (1)

1-6: Avoid network in unit tests; mock GeoTIFF lookups.

Stub getGeoTiffValue to keep tests hermetic and fast; put integration tests behind a separate flag/suite.

Example:

+import * as geotiff from "../../../../shared/geotiff"
+vi.spyOn(geotiff, "getGeoTiffValue").mockResolvedValue(0) // adjust per case
fdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.ts (1)

57-67: Add rounding and include details for traceability.

Return rounded values and note whether p_n_rt came from fertilizer or table11 fallback.

Apply:

-            let normFilling = new Decimal(0)
+            let normFilling = new Decimal(0)
+            let normFillingDetails: string | undefined
...
-                const p_n_rt = new Decimal(
-                    fertilizer.p_n_rt ?? rvoTypeProperties.p_n_rt ?? 0,
-                )
+                const p_n_rt_src = fertilizer.p_n_rt ?? rvoTypeProperties.p_n_rt ?? 0
+                const p_n_rt = new Decimal(p_n_rt_src)
+                normFillingDetails = `p_n_rt=${p_n_rt_src} source=${fertilizer.p_n_rt != null ? "fertilizer" : "table11"}`
...
-            acc.applicationFilling.push({
+            acc.applicationFilling.push({
                 p_app_id: application.p_app_id,
-                normFilling: normFilling.toNumber(),
+                normFilling: normFilling.toDecimalPlaces(3).toNumber(),
+                ...(normFillingDetails ? { normFillingDetails } : {}),
             })
...
-    return {
-        normFilling: totalFilling.toNumber(),
+    return {
+        normFilling: totalFilling.toDecimalPlaces(3).toNumber(),
         applicationFilling,
     }

Also applies to: 86-91

fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (1)

158-162: Add guard or simplify fosfaatNormsData structure; current [0] indexing lacks explanation.

The data is a single-element array with no documented reason for the array wrapper. There is also no guard for empty array. Recommend either:

  1. Export fosfaatNormsData directly as an object (not wrapped in array), eliminating [0]
  2. Or add a guard and comment explaining the array design:
-const normsForKlasse = fosfaatNormsData[0][fosfaatKlasse]
+// Single-element array for [future extensibility reason]
+const normsData = fosfaatNormsData[0]
+if (!normsData) {
+    throw new Error("Phosphate norms data is missing.")
+}
+const normsForKlasse = normsData[fosfaatKlasse]

Also, correct the comment at line 130 (file is .ts, not .json).

fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.test.ts (2)

119-171: Deduplicate the double “Example 2” tests.

These two tests cover the same scenario with similar assertions. Consider consolidating into one test with detailed breakdown asserts to reduce maintenance.


80-94: Also assert applicationFilling order to catch regressions.

Add an expectation that the per‑application results preserve input order (by p_app_id). This guards against accidental reordering in the filling algorithm.

Example:

expect(result.applicationFilling.map(a => a.p_app_id))
  .toEqual(applications.map(a => a.p_app_id))
fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.test.ts (2)

10-35: Avoid network I/O in unit tests; mock geo lookups.

Directly calling GeoTIFF endpoints can make tests flaky and slow. Stub getGeoTiffValue/getRegion/isFieldInNVGebied for deterministic runs; keep a separate integration test if needed.


37-41: Minor: clean up describe/spacing.

Remove leading space in describe title and extra spaces before function calls for readability.

fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts (2)

120-139: Eliminate O(n²) findIndex; carry originalIndex for standard apps.

Keep original order without scans by storing originalIndex like you already do for organic-rich entries.

@@
-    const standardApplications: {
+    const standardApplications: {
         application: FertilizerApplication
         p_p_rt: Decimal
         p_app_amount: Decimal
-    }[] = []
+        originalIndex: number
+    }[] = []
@@
-    applications.forEach((application, index) => {
+    applications.forEach((application, index) => {
@@
-        } else {
-            standardApplications.push({ application, p_p_rt, p_app_amount })
+        } else {
+            standardApplications.push({ application, p_p_rt, p_app_amount, originalIndex: index })
         }
     })
@@
-    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] = {
             p_app_id: application.p_app_id,
             normFilling: normFilling.toNumber(),
         }
     }

Also applies to: 60-66, 75-113


168-176: Clarify details: factor is “meegeteld”, not “korting”.

The factor represents counted share (25%/75%), not discount. Update message text.

-                normFillingDetails = `OS-rijke meststof (${discountFactor.times(
-                    100,
-                )}% korting) draagt ${phosphateToDiscount
+                normFillingDetails = `OS-rijke meststof (${discountFactor.times(
+                    100,
+                )}% meegeteld) draagt ${phosphateToDiscount
                     .times(discountFactor)
                     .toFixed(2)}kg bij aan de norm.`
@@
-                normFillingDetails =
-                    "OS-rijke meststof, geen korting toegepast."
+                normFillingDetails =
+                    "OS-rijke meststof, 100% meegeteld (geen korting)."

Also applies to: 174-176

fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts (2)

33-41: Speed up fertilizer lookup with a Map (mirrors phosphate file).

Avoid per-iteration .find by prebuilding a Map keyed by p_id_catalogue.

@@
-    for (const application of applications) {
-        const fertilizer = fertilizers.find(
-            (f) => f.p_id_catalogue === application.p_id_catalogue,
-        )
+    const fertilizersMap = new Map(
+        fertilizers.map((f) => [f.p_id_catalogue, f]),
+    )
+    for (const application of applications) {
+        const fertilizer = fertilizersMap.get(application.p_id_catalogue)
         if (!fertilizer) {
             throw new Error(
                 `Fertilizer ${application.p_id_catalogue} not found for application ${application.p_app_id}`,
             )
         }

Also applies to: 38-41


216-234: Isolate “applicationPeriod” logic to a helper for clarity/reuse.

Extract the Sep–Jan window check to a small util (and consider parsing a normalized period spec) to keep table-driven rules clean. Behavior unchanged.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d178e6 and e2be107.

📒 Files selected for processing (14)
  • fdm-calculator/src/norms/index.test.ts (2 hunks)
  • fdm-calculator/src/norms/index.ts (2 hunks)
  • fdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.test.ts (1 hunks)
  • fdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.ts (1 hunks)
  • fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.test.ts (1 hunks)
  • fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts (1 hunks)
  • fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.test.ts (1 hunks)
  • fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts (1 hunks)
  • fdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.test.ts (2 hunks)
  • fdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.ts (2 hunks)
  • fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.test.ts (3 hunks)
  • fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (1 hunks)
  • fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.test.ts (38 hunks)
  • fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.test.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-21T12:06:07.070Z
Learnt from: SvenVw
PR: SvenVw/fdm#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/fosfaatgebruiksnorm.test.ts
  • fdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.ts
  • fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts
  • fdm-calculator/src/norms/index.ts
📚 Learning: 2025-10-22T08:09:17.708Z
Learnt from: SvenVw
PR: SvenVw/fdm#285
File: fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts:6-9
Timestamp: 2025-10-22T08:09:17.708Z
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
🧬 Code graph analysis (11)
fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.test.ts (2)
fdm-calculator/src/norms/nl/2025/value/types.d.ts (1)
  • NL2025NormsInput (14-26)
fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (1)
  • calculateNL2025FosfaatGebruiksNorm (138-173)
fdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.ts (1)
fdm-calculator/src/norms/nl/2025/filling/types.d.ts (2)
  • NL2025NormsFillingInput (47-55)
  • NormFilling (10-17)
fdm-calculator/src/norms/index.test.ts (7)
fdm-calculator/src/norms/index.ts (1)
  • createFunctionsForNorms (15-29)
fdm-calculator/src/norms/nl/2025/value/input.ts (1)
  • collectNL2025InputForNorms (25-94)
fdm-calculator/src/norms/nl/2025/filling/input.ts (1)
  • collectInputForFertilizerApplicationFilling (26-98)
fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts (1)
  • calculateNL2025FertilizerApplicationFillingForNitrogen (22-116)
fdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.ts (1)
  • calculateNL2025FertilizerApplicationFillingForManure (16-91)
fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts (1)
  • calculateNL2025FertilizerApplicationFillingForPhosphate (34-221)
fdm-calculator/src/norms/farm.ts (1)
  • aggregateNormFillingsToFarmLevel (169-200)
fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.test.ts (2)
fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts (1)
  • calculateNL2025FertilizerApplicationFillingForPhosphate (34-221)
fdm-calculator/src/norms/nl/2025/filling/types.d.ts (1)
  • NL2025NormsFillingInput (47-55)
fdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.test.ts (2)
fdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.ts (1)
  • calculateNL2025FertilizerApplicationFillingForManure (16-91)
fdm-calculator/src/norms/nl/2025/filling/types.d.ts (1)
  • NL2025NormsFillingInput (47-55)
fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts (3)
fdm-calculator/src/norms/nl/2025/filling/types.d.ts (3)
  • NL2025NormsFillingInput (47-55)
  • NormFilling (10-17)
  • WorkingCoefficientDetails (41-45)
fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (1)
  • getRegion (69-100)
fdm-calculator/src/norms/nl/2025/value/types.d.ts (1)
  • RegionKey (147-147)
fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.test.ts (2)
fdm-calculator/src/norms/nl/2025/value/types.d.ts (2)
  • NL2025NormsInput (14-26)
  • NL2025NormsInputForCultivation (6-9)
fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (1)
  • calculateNL2025StikstofGebruiksNorm (532-644)
fdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.test.ts (2)
fdm-calculator/src/norms/nl/2025/value/types.d.ts (1)
  • NL2025NormsInput (14-26)
fdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.ts (1)
  • calculateNL2025DierlijkeMestGebruiksNorm (149-193)
fdm-calculator/src/norms/index.ts (9)
fdm-calculator/src/norms/nl/2025/value/input.ts (1)
  • collectNL2025InputForNorms (25-94)
fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (1)
  • calculateNL2025StikstofGebruiksNorm (532-644)
fdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.ts (1)
  • calculateNL2025DierlijkeMestGebruiksNorm (149-193)
fdm-calculator/src/norms/nl/2025/value/fosfaatgebruiksnorm.ts (1)
  • calculateNL2025FosfaatGebruiksNorm (138-173)
fdm-calculator/src/norms/nl/2025/filling/input.ts (1)
  • collectInputForFertilizerApplicationFilling (26-98)
fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts (1)
  • calculateNL2025FertilizerApplicationFillingForNitrogen (22-116)
fdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.ts (1)
  • calculateNL2025FertilizerApplicationFillingForManure (16-91)
fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts (1)
  • calculateNL2025FertilizerApplicationFillingForPhosphate (34-221)
fdm-calculator/src/norms/farm.ts (1)
  • aggregateNormFillingsToFarmLevel (169-200)
fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts (2)
fdm-calculator/src/norms/nl/2025/filling/types.d.ts (2)
  • NL2025NormsFillingInput (47-55)
  • NormFilling (10-17)
fdm-calculator/src/norms/nl/2025/filling/table-11-mestcodes.ts (1)
  • table11Mestcodes (3-336)
fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (1)
fdm-calculator/src/norms/nl/2025/value/types.d.ts (2)
  • NL2025NormsInput (14-26)
  • GebruiksnormResult (160-170)
🔇 Additional comments (7)
fdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.ts (2)

136-141: “Within 100m” behavior unclear.

Docs mention 100m proximity, but code checks only the centroid raster cell. Verify the raster already encodes the 100m buffer; if not, either implement a buffer check or adjust the remark.

Provide a brief note or data source confirming whether the Natura 2000 TIFF includes the 100m buffer.


25-25: Verify 2024 raster data currency for GWBG and Natura 2000 in NL2025 module.

Verification found 2024 raster references at lines 25 and 60 in dierlijke-mest-gebruiksnorm.ts. The codebase shows a mixed pattern: some NL2025 modules reference 2025 data (e.g., nv.tiff, derogatievrije_zones.tiff), while others reuse 2024 data (e.g., grondsoorten.tiff, gwbg.tiff, natura2000.tiff). No 2025 versions of gwbg or natura2000 rasters exist in the codebase.

Since GWBG (groundwater protection areas) and Natura 2000 (EU protected sites) are relatively static geospatial designations that don't change annually, the 2024 versions may be intentionally reused. However, confirm whether these datasets remain authoritative for 2025 or should be updated to maintain consistency with the NL2025 module year.

fdm-calculator/src/norms/nl/2025/filling/dierlijke-mest-gebruiksnorm.test.ts (1)

1-188: Solid, focused coverage of manure filling.

Happy-path, edge, and error scenarios are well covered.

fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (1)

72-79: Confirm using 2024 grondsoorten.tiff for 2025 is intended.

If the official soil-type raster is unchanged, this is fine; otherwise, switch to the 2025 asset.

Based on learnings

fdm-calculator/src/norms/index.ts (1)

15-24: API wiring looks solid for NL 2025.

Exports are consistent; mappings for collect/calculate functions align with module structure.

Based on learnings

fdm-calculator/src/norms/nl/2025/filling/fosfaatgebruiksnorm.ts (1)

6-8: RVO mestcode groupings look correct.

Lists align with the intended 2025 stimulus groupings incl. organic-only set. Based on learnings

fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts (1)

92-99: Guard p_app_amount presence.

If p_app_amount can be null/undefined, add a default 0 or throw early to avoid Decimal construction errors.

Comment thread fdm-calculator/src/norms/nl/2025/value/dierlijke-mest-gebruiksnorm.test.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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2be107 and 5c78e79.

📒 Files selected for processing (2)
  • fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.test.ts (1 hunks)
  • fdm-calculator/src/norms/nl/2025/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
🧬 Code graph analysis (1)
fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.test.ts (4)
fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts (3)
  • isBouwland (114-137)
  • getWorkingCoefficient (152-243)
  • calculateNL2025FertilizerApplicationFillingForNitrogen (22-103)
fdm-calculator/src/norms/nl/2025/value/types.d.ts (1)
  • RegionKey (147-147)
fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (1)
  • getRegion (69-100)
fdm-calculator/src/norms/nl/2025/filling/types.d.ts (1)
  • NL2025NormsFillingInput (47-55)
🔇 Additional comments (4)
fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.test.ts (4)

1-19: LGTM!

The import structure and mock setup are well-organized. Mocking getRegion is appropriate since it involves external GeoTIFF lookups.


21-97: LGTM!

Comprehensive test coverage for isBouwland logic with clear edge cases: non-bouwland codes, date boundaries, and inactive cultivations.


99-172: LGTM!

Excellent test coverage for getWorkingCoefficient across multiple fertilizer types, soil types, grazing intentions, and application periods. The tests are well-organized and expectations are clearly documented.

Also applies to: 217-540


542-768: LGTM!

Comprehensive integration tests for calculateNL2025FertilizerApplicationFillingForNitrogen with proper mock setup, clear test cases covering single/multiple applications, table fallbacks, error handling, and bouwland date-specific logic. The calculations are well-documented with expected values.

Comment thread fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.test.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 (1)
fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.test.ts (1)

500-503: Add default mock setup for getRegion in beforeEach.

While the module mock at line 17 creates a mock function, it doesn't provide a default return value. Tests at lines 505-671 don't explicitly mock getRegion, resulting in undefined being passed as soilType to getWorkingCoefficient. Although this currently works because those tests use fertilizer types that don't require specific soil types, it creates fragile and inconsistent test setup.

Apply this diff to add a consistent default mock setup:

 describe("calculateNL2025FertilizerApplicationFillingForNitrogen", () => {
+    beforeEach(() => {
+        vi.mocked(getRegion).mockResolvedValue("zand_nwc")
+    })
+
     afterEach(() => {
         vi.clearAllMocks()
     })

This ensures all tests have a predictable default soil type, while tests requiring specific regions (like lines 717, 674) can override it.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c78e79 and ab42236.

📒 Files selected for processing (1)
  • fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.test.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.test.ts (4)
fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.ts (3)
  • isBouwland (114-137)
  • getWorkingCoefficient (152-243)
  • calculateNL2025FertilizerApplicationFillingForNitrogen (22-103)
fdm-calculator/src/norms/nl/2025/value/types.d.ts (1)
  • RegionKey (147-147)
fdm-calculator/src/norms/nl/2025/filling/types.d.ts (1)
  • NL2025NormsFillingInput (47-55)
fdm-calculator/src/norms/nl/2025/value/stikstofgebruiksnorm.ts (1)
  • getRegion (69-100)
🔇 Additional comments (3)
fdm-calculator/src/norms/nl/2025/filling/stikstofgebruiksnorm.test.ts (3)

21-97: LGTM! Comprehensive coverage of isBouwland logic.

The test suite thoroughly covers all relevant scenarios including bouwland/non-bouwland codes, active cultivation detection, and date boundary conditions. The test assertions correctly validate the expected behavior.


99-498: LGTM! Excellent comprehensive coverage of working coefficient logic.

The test suite thoroughly validates all working coefficient scenarios including default behaviors, fertilizer type distinctions (on-farm vs aangevoerd), soil type dependencies, application period rules, and grazing intention impacts. The nested describe blocks provide clear organization for related test cases.


505-766: Excellent end-to-end test coverage.

The test suite comprehensively validates the nitrogen filling calculation including single and multiple applications, nitrogen content derivation from both p_n_rt and table 11 fallback, error handling, onFarmProduced logic based on grazing intention, and bouwland-specific working coefficient rules. The tests correctly verify both aggregate normFilling and per-application details.

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 (3)
fdm-docs/docs/norms/nl/2025/01-index.md (1)

27-39: Fix unordered list indentation to comply with markdownlint (MD007).

The nested list items use 4-space indentation, but the project's markdownlint configuration expects 2 spaces. This affects lines 28–39 (sub-items under bullet points).

Apply this diff to fix the indentation:

  ## How the Calculator Works: Required Data
  
  To calculate the norms for a specific field, the FDM Calculator requires the following information for the 2025 calendar year.
  
  *   **Farm Details**:
-     *   **Derogation Status**: Does the farm have a derogation permit for 2025? This is a critical factor for nitrogen and animal manure norms.
+   *   **Derogation Status**: Does the farm have a derogation permit for 2025? This is a critical factor for nitrogen and animal manure norms.
  
  *   **Field & Location**:
-     *   **Field Location**: The precise geographical coordinates of the field are used to determine if it falls within special regulatory zones, such as Nutrient-Polluted Areas (`NV-gebieden`), Groundwater Protection Areas (`GWBG-gebieden`), or specific soil regions (sand, clay, peat, loess).
+   *   **Field Location**: The precise geographical coordinates of the field are used to determine if it falls within special regulatory zones, such as Nutrient-Polluted Areas (`NV-gebieden`), Groundwater Protection Areas (`GWBG-gebieden`), or specific soil regions (sand, clay, peat, loess).
  
  *   **Cultivation Plan for 2025**:
-     *   **Main Crop (`hoofdteelt`)**: The primary crop grown on the field. The calculator identifies the main crop as the one with the longest cultivation period between May 15th and July 15th.
-     *   **Crop Variety**: For certain crops like potatoes and flowers, the specific variety can result in a higher or lower nitrogen norm.
-     *   **Cultivation Dates**: The start and end dates of cultivation are crucial for time-sensitive norms, such as those for temporary grassland.
+   *   **Main Crop (`hoofdteelt`)**: The primary crop grown on the field. The calculator identifies the main crop as the one with the longest cultivation period between May 15th and July 15th.
+   *   **Crop Variety**: For certain crops like potatoes and flowers, the specific variety can result in a higher or lower nitrogen norm.
+   *   **Cultivation Dates**: The start and end dates of cultivation are crucial for time-sensitive norms, such as those for temporary grassland.
  
  *   **Latest Soil Analysis Data**:
-     *   **Phosphate Levels**: The P-CaCl2 (or P-PAE) and P-Al values from your most recent soil test are used to classify the soil's phosphate status, which directly determines the phosphate usage norm.
+   *   **Phosphate Levels**: The P-CaCl2 (or P-PAE) and P-Al values from your most recent soil test are used to classify the soil's phosphate status, which directly determines the phosphate usage norm.
fdm-docs/docs/norms/nl/2025/fosfaatgebruiksnorm.md (2)

52-70: Fix unordered list indentation to comply with markdownlint (MD007).

Lines 57–66 and 69 use 4-space indentation for nested list items, but the project's markdownlint configuration expects 2 spaces.

Apply this diff to fix the indentation:

  #### Differentiated Percentages for Organic-Rich Fertilizers:
  
  The phosphate in qualifying organic-rich fertilizers counts towards the usage norm according to the following differentiated percentages:
  
  *   **25% of phosphate counts for:**
-     *   GFT-compost
-     *   Groencompost (Green compost)
+   *   GFT-compost
+   *   Groencompost (Green compost)
  
  *   **75% of phosphate counts for:**
-     *   Vaste strorijke mest van rundvee (Straw-rich solid manure from cattle)
-     *   Vaste strorijke mest van varkens (only for organic farms) (Straw-rich solid manure)
-     *   Vaste strorijke mest van schapen (Straw-rich solid manure from sheep)
-     *   Vaste strorijke mest van geiten (Straw-rich solid manure from goats)
-     *   Vaste strorijke mest van paarden (Straw-rich solid manure from horses)
-     *   Champost
+   *   Vaste strorijke mest van rundvee (Straw-rich solid manure from cattle)
+   *   Vaste strorijke mest van varkens (only for organic farms) (Straw-rich solid manure)
+   *   Vaste strorijke mest van schapen (Straw-rich solid manure from sheep)
+   *   Vaste strorijke mest van geiten (Straw-rich solid manure from goats)
+   *   Vaste strorijke mest van paarden (Straw-rich solid manure from horses)
+   *   Champost
  
  *   **100% of phosphate counts for:**
-     *   All other fertilizers, including mineral fertilizers and other organic fertilizers not listed above.
+   *   All other fertilizers, including mineral fertilizers and other organic fertilizers not listed above.

80-80: Use proper markdown headings instead of bold emphasis for examples (MD036).

Lines 80 and 93 use bold text (**Example 1:** and **Example 2:**) as section headers. Convert these to proper markdown headings (e.g., ### Example 1:) to comply with markdownlint's MD036 rule.

Apply this diff:

  #### Calculation Examples:
  
- **Example 1: Standard Application**
+ ### Example 1: Standard Application
  *   **Parcel**: Arable land, phosphate status `Ruim`.

  ...

- **Example 2: High Application of Compost**
+ ### Example 2: High Application of Compost
  *   **Parcel**: Grassland, phosphate status `Arm`.

Also applies to: 93-93

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf89a92 and 2413e2b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • .changeset/short-hats-poke.md (1 hunks)
  • fdm-app/package.json (1 hunks)
  • fdm-docs/blog/2025-07-31-release-notes.mdx (1 hunks)
  • fdm-docs/blog/tags.yml (1 hunks)
  • fdm-docs/docs/norms/nl/01-2025.md (0 hunks)
  • fdm-docs/docs/norms/nl/2025/01-index.md (1 hunks)
  • fdm-docs/docs/norms/nl/2025/_category_.json (1 hunks)
  • fdm-docs/docs/norms/nl/2025/dierlijke-mest-gebruiksnorm.md (1 hunks)
  • fdm-docs/docs/norms/nl/2025/fosfaatgebruiksnorm.md (1 hunks)
  • fdm-docs/docs/norms/nl/2025/stikstofgebruiksnorm.md (1 hunks)
  • fdm-docs/docs/norms/nl/_category_.json (1 hunks)
💤 Files with no reviewable changes (1)
  • fdm-docs/docs/norms/nl/01-2025.md
✅ Files skipped from review due to trivial changes (2)
  • fdm-docs/docs/norms/nl/2025/category.json
  • fdm-docs/blog/2025-07-31-release-notes.mdx
🚧 Files skipped from review as they are similar to previous changes (1)
  • fdm-app/package.json
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-21T12:06:07.070Z
Learnt from: SvenVw
PR: SvenVw/fdm#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-docs/docs/norms/nl/2025/01-index.md
  • fdm-docs/docs/norms/nl/2025/stikstofgebruiksnorm.md
  • fdm-docs/docs/norms/nl/2025/dierlijke-mest-gebruiksnorm.md
  • fdm-docs/docs/norms/nl/2025/fosfaatgebruiksnorm.md
🪛 LanguageTool
fdm-docs/docs/norms/nl/2025/stikstofgebruiksnorm.md

[grammar] ~24-~24: Ensure spelling is correct
Context: ...oked up in the official RVO Table 2 (or Tabel 2g for NV-gebieden). 4. **Apply Specif...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

fdm-docs/docs/norms/nl/2025/dierlijke-mest-gebruiksnorm.md

[grammar] ~55-~55: Ensure spelling is correct
Context: ...odes (mestcodes). Example from RVO Tabel 11: | Diersoort (Animal Species) | O...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~72-~72: Ensure spelling is correct
Context: ... known, it will be used; otherwise, the forfaitair content is applied.

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
fdm-docs/docs/norms/nl/2025/01-index.md

28-28: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


31-31: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


34-34: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


35-35: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


36-36: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


39-39: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)

fdm-docs/docs/norms/nl/2025/fosfaatgebruiksnorm.md

57-57: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


58-58: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


61-61: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


62-62: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


63-63: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


64-64: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


65-65: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


66-66: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


69-69: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


80-80: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


93-93: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🔇 Additional comments (5)
fdm-docs/blog/tags.yml (1)

26-29: LGTM!

The new fdm-app tag is well-structured, follows the established pattern of other tag entries, and appropriately categorizes content related to the React web application. The description correctly references the @svenvw/fdm-app package, and the permalink is consistent with the naming convention.

fdm-docs/docs/norms/nl/_category_.json (1)

1-4: Approved: Category metadata update.

fdm-docs/docs/norms/nl/2025/dierlijke-mest-gebruiksnorm.md (1)

1-72: Well‑structured documentation with comprehensive coverage.

The document clearly explains the animal manure norm determination and filling calculations, with concrete examples and proper references to RVO Tabel 11. The static analysis flagged potential spelling issues (lines 55, 72), but these appear to be false positives for Dutch agricultural terminology (RVO, Tabel, forfaitair), which is appropriate for this documentation.

fdm-docs/docs/norms/nl/2025/stikstofgebruiksnorm.md (1)

1-107: Excellent comprehensive documentation with detailed examples and tables.

The document thoroughly explains nitrogen norm calculation steps, catch crop/winter crop reduction rules, and filling logic with efficiency coefficients. The efficiency coefficient table (lines 85–103) is well‑structured and references RVO Tabel 9 appropriately. The static analysis flagged a potential spelling issue at line 24, but this appears to be a false positive for Dutch regulatory terminology.

.changeset/short-hats-poke.md (1)

1-5: Approved: Changeset entry.

The changeset correctly declares a minor version bump for @svenvw/fdm-docs with a clear description of the documentation updates for the 2025 Dutch Fertilizer Application Norms.

Comment thread fdm-calculator/src/norms/nl/2025/filling/table-11-mestcodes.ts
Comment thread fdm-calculator/src/norms/nl/2025/filling/table-11-mestcodes.ts
Comment thread fdm-core/src/db/schema.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.

that was a long one to review. Valuable addition!
No big issues found. Well done.

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) enhancement New feature or request fdm-app fdm-calculator fdm-core fdm-data

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement 2025 Dutch Fertilizer Norm Calculation for Filling

2 participants