Skip to content

Show specific error messages for balance calculations on empty farms#546

Merged
SvenVw merged 6 commits into
release/2026-03from
organization-balance-empty-farm
Apr 2, 2026
Merged

Show specific error messages for balance calculations on empty farms#546
SvenVw merged 6 commits into
release/2026-03from
organization-balance-empty-farm

Conversation

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator

@BoraIneviNMI BoraIneviNMI commented Apr 2, 2026

Enhancements

  • If a farm has no fields its balance calculation error message will clearly indicate that it has no fields.
  • When calculating organization balance the default farm selection will not include the empty farms.

Summary by CodeRabbit

Bug Fixes

  • Improved error messages for nitrogen and organic matter balance calculations, providing specific guidance (e.g., "Geen percelen gevonden in dit bedrijf.") instead of generic fallback text when no fields are available.
  • Enhanced farm filtering logic to properly exclude farms without input data from calculations and selection dialogs.
  • Consistent error handling across the system for empty field inputs.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 2, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • main
  • development

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 39807eff-a240-4282-b9b1-fbf6fa7ab663

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch organization-balance-empty-farm

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.

@sentry
Copy link
Copy Markdown

sentry Bot commented Apr 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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: 5

🧹 Nitpick comments (3)
fdm-calculator/src/balance/organic-matter/index.test.ts (1)

49-63: Expand this test to validate hasErrors and top-level errorMessage.

The new UI path depends on errorMessage, so validating only fieldErrorMessages leaves a regression gap.

💡 Suggested test update
-        it("should return correct error message for no fields in input", async () => {
-            expect(
-                (
-                    await calculateOrganicMatterBalance(mockFdm, {
-                        fertilizerDetails: [],
-                        cultivationDetails: [],
-                        fields: [],
-                        timeFrame: {
-                            start: new Date("2023-01-01"),
-                            end: new Date("2023-12-31"),
-                        },
-                    })
-                ).fieldErrorMessages,
-            ).toContain("No fields in input")
-        })
+        it("should return correct error payload for no fields in input", async () => {
+            const result = await calculateOrganicMatterBalance(mockFdm, {
+                fertilizerDetails: [],
+                cultivationDetails: [],
+                fields: [],
+                timeFrame: {
+                    start: new Date("2023-01-01"),
+                    end: new Date("2023-12-31"),
+                },
+            })
+
+            expect(result.hasErrors).toBe(true)
+            expect(result.errorMessage).toBe("No fields in input")
+            expect(result.fieldErrorMessages).toContain("No fields in input")
+        })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fdm-calculator/src/balance/organic-matter/index.test.ts` around lines 49 -
63, The test for calculateOrganicMatterBalance that currently asserts only
fieldErrorMessages should be expanded to also assert the top-level error flags:
call calculateOrganicMatterBalance with empty fields (as in the diff) and assert
that the returned object's hasErrors property is true and that the top-level
errorMessage contains or equals "No fields in input" (in addition to the
existing assertion on fieldErrorMessages) so the UI path that reads errorMessage
is covered.
fdm-calculator/src/balance/nitrogen/index.test.ts (1)

252-266: Strengthen this empty-input test to cover the full error contract.

This case now drives UI behavior via top-level errorMessage; assert hasErrors and errorMessage in addition to fieldErrorMessages to prevent regressions.

💡 Suggested test update
-    it("should return correct error message for no fields in input", async () => {
-        expect(
-            (
-                await calculateNitrogenBalance(mockFdm, {
-                    fertilizerDetails: [],
-                    cultivationDetails: [],
-                    fields: [],
-                    timeFrame: {
-                        start: new Date("2023-01-01"),
-                        end: new Date("2023-12-31"),
-                    },
-                })
-            ).fieldErrorMessages,
-        ).toContain("No fields in input")
-    })
+    it("should return correct error payload for no fields in input", async () => {
+        const result = await calculateNitrogenBalance(mockFdm, {
+            fertilizerDetails: [],
+            cultivationDetails: [],
+            fields: [],
+            timeFrame: {
+                start: new Date("2023-01-01"),
+                end: new Date("2023-12-31"),
+            },
+        })
+
+        expect(result.hasErrors).toBe(true)
+        expect(result.errorMessage).toBe("No fields in input")
+        expect(result.fieldErrorMessages).toContain("No fields in input")
+    })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fdm-calculator/src/balance/nitrogen/index.test.ts` around lines 252 - 266,
The test for empty input should assert the full error contract: after calling
calculateNitrogenBalance(mockFdm, {...}) verify that the returned object has
hasErrors set to true and errorMessage equals the expected top-level message (in
addition to the existing assertion on fieldErrorMessages), so update the spec to
check response.hasErrors and response.errorMessage alongside
response.fieldErrorMessages to prevent regressions in UI behavior.
fdm-app/app/routes/organization.$slug.$calendar.balance.organic-matter._index.tsx (1)

248-248: Consider extracting the magic string to a shared constant.

The error message "No fields in input" is used as both a value (line 248) and a comparison key (lines 465-466). Since this string is also used in the nitrogen route and potentially in fdm-calculator, extracting it to a shared constant would improve maintainability and reduce the risk of typos causing silent failures.

💡 Example approach

Create a shared constant (e.g., in a constants file or import from @nmi-agro/fdm-calculator if defined there):

// In a shared constants file or exported from fdm-calculator
export const ERROR_NO_FIELDS_IN_INPUT = "No fields in input"

Then use it in both locations for consistency.

Also applies to: 465-468

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@fdm-app/app/routes/organization`.$slug.$calendar.balance.organic-matter._index.tsx
at line 248, Extract the literal "No fields in input" into a shared exported
constant (for example ERROR_NO_FIELDS_IN_INPUT) and replace all occurrences
instead of using the magic string; update the place that sets errorMessage
(symbol: errorMessage) and the places that compare against that value (the
comparison in this route and the similar check in the nitrogen route /
fdm-calculator usage) to import and use the shared constant so both assignment
and comparisons reference the same identifier.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@fdm-app/app/routes/organization`.$slug.$calendar.balance.nitrogen._index.tsx:
- Line 338: The Suspense/key prop string contains an extra closing brace in the
template literal used for the key; update the key value that references
loaderData.organization.id and location.search (the
key={`${loaderData.organization.id},${location.search}}`} usage) to remove the
stray `}` so it becomes a well-formed template literal combining those two
values.

In
`@fdm-app/app/routes/organization`.$slug.$calendar.balance.organic-matter._index.tsx:
- Line 343: Fix the extra closing brace in the Suspense/key template literal:
locate the key prop that currently reads
key={`${loaderData.organization.id},${location.search}}`} (in the component
rendering Suspense) and remove the trailing `}` so the template becomes
`${loaderData.organization.id},${location.search}` producing
key={`${loaderData.organization.id},${location.search}`}.
- Around line 312-314: The returned farmIds are built from the unsorted farms
array causing order mismatch with the area-sorted farmsExtended used earlier;
update the return to use the already-computed farmIds (or map/filter from
farmsExtended) instead of filtering/mapping farms so asyncData.farmIds preserves
the display order (refer to the farmIds variable and farmsExtended used around
lines where balance calculations occur).

In `@fdm-calculator/src/balance/nitrogen/index.ts`:
- Around line 82-100: The fallback for empty input currently creates a synthetic
placeholder field (with b_id: "" etc.) and passes it to
calculateNitrogenBalancesFieldToFarm, which can leak a phantom field downstream;
instead, detect fieldInputs.length === 0 and call
calculateNitrogenBalancesFieldToFarm with an empty array ([]) so aggregation
runs over no fields, preserving the errorMessage return; update the return to
stop injecting any fake field and pass [] to
calculateNitrogenBalancesFieldToFarm while keeping the existing errorMessage and
flag parameters.

In `@fdm-calculator/src/balance/organic-matter/index.ts`:
- Around line 87-105: The current early-return creates a fake field object
(b_id: "") which can leak into downstream UIs; instead, return an empty fields
array and surface the error via the existing error signaling used by the
aggregator. Replace the block that checks fieldInputs.length === 0 so it returns
{ errorMessage, ...calculateOrganicMatterBalancesFieldToFarm([], true,
[errorMessage]) } (i.e. pass an empty array to
calculateOrganicMatterBalancesFieldToFarm and preserve
errorMessage/fieldErrorMessages) rather than constructing a placeholder field;
ensure you reference fieldInputs, errorMessage, and
calculateOrganicMatterBalancesFieldToFarm when making the change.

---

Nitpick comments:
In
`@fdm-app/app/routes/organization`.$slug.$calendar.balance.organic-matter._index.tsx:
- Line 248: Extract the literal "No fields in input" into a shared exported
constant (for example ERROR_NO_FIELDS_IN_INPUT) and replace all occurrences
instead of using the magic string; update the place that sets errorMessage
(symbol: errorMessage) and the places that compare against that value (the
comparison in this route and the similar check in the nitrogen route /
fdm-calculator usage) to import and use the shared constant so both assignment
and comparisons reference the same identifier.

In `@fdm-calculator/src/balance/nitrogen/index.test.ts`:
- Around line 252-266: The test for empty input should assert the full error
contract: after calling calculateNitrogenBalance(mockFdm, {...}) verify that the
returned object has hasErrors set to true and errorMessage equals the expected
top-level message (in addition to the existing assertion on fieldErrorMessages),
so update the spec to check response.hasErrors and response.errorMessage
alongside response.fieldErrorMessages to prevent regressions in UI behavior.

In `@fdm-calculator/src/balance/organic-matter/index.test.ts`:
- Around line 49-63: The test for calculateOrganicMatterBalance that currently
asserts only fieldErrorMessages should be expanded to also assert the top-level
error flags: call calculateOrganicMatterBalance with empty fields (as in the
diff) and assert that the returned object's hasErrors property is true and that
the top-level errorMessage contains or equals "No fields in input" (in addition
to the existing assertion on fieldErrorMessages) so the UI path that reads
errorMessage is covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6340979f-cbd7-4bee-8e8b-a65e07ba19c4

📥 Commits

Reviewing files that changed from the base of the PR and between 8496b60 and 21d7902.

📒 Files selected for processing (10)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.organic-matter._index.tsx
  • fdm-app/app/routes/organization.$slug.$calendar.balance.nitrogen._index.tsx
  • fdm-app/app/routes/organization.$slug.$calendar.balance.organic-matter._index.tsx
  • fdm-calculator/src/balance/nitrogen/index.test.ts
  • fdm-calculator/src/balance/nitrogen/index.ts
  • fdm-calculator/src/balance/nitrogen/types.d.ts
  • fdm-calculator/src/balance/organic-matter/index.test.ts
  • fdm-calculator/src/balance/organic-matter/index.ts
  • fdm-calculator/src/balance/organic-matter/types.ts

Comment thread fdm-app/app/routes/organization.$slug.$calendar.balance.nitrogen._index.tsx Outdated
Comment thread fdm-app/app/routes/organization.$slug.$calendar.balance.organic-matter._index.tsx Outdated
Comment thread fdm-app/app/routes/organization.$slug.$calendar.balance.organic-matter._index.tsx Outdated
Comment thread fdm-calculator/src/balance/nitrogen/index.ts
Comment thread fdm-calculator/src/balance/organic-matter/index.ts
@BoraIneviNMI
Copy link
Copy Markdown
Collaborator Author

@coderabbitai resume

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 2, 2026

✅ Actions performed

Reviews resumed.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@fdm-app/app/routes/organization`.$slug.$calendar.balance.nitrogen._index.tsx:
- Around line 191-202: The current default-selection logic can yield farmIds =
[] (if farmIdsSet stays empty), which hides the empty-input state; after
computing farmIds from farmIdsSet/farmsExtended, add an explicit check for
farmIds.length === 0 and handle it by setting/returning the "No fields in input"
error state (or a dedicated flag) so the rendering code that reads farmResults
will show the empty-input UI instead of proceeding with a normal calculation.
Ensure you update the code paths that use farmResults to honor this new
flag/error and reference farmIdsSet, farmsExtended, farmIds and farmResults when
making the change.

In
`@fdm-app/app/routes/organization`.$slug.$calendar.balance.organic-matter._index.tsx:
- Around line 191-201: The current logic yields farmIds = [] when no query is
present and every accessible farm has zero fields, causing downstream
farmResults = [] and hiding the "empty-input" error; detect this case right
after computing farmIdsSet/farmsExtended (when farmIdsSet.size === 0 &&
farmsExtended.length > 0) and short-circuit by returning an explicit error/flag
instead of producing an empty farmIds array—e.g., throw or return a structured
error Response (with a clear message like "organization has no fields") or set
farmIds to a sentinel (null/undefined plus an error flag) so consumers (the code
that reads farmResults) surface the empty-input error instead of rendering a
blank list. Ensure you update the code paths that reference farmIds/farmResults
to handle this error sentinel/Response accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cdf54646-33c9-470e-b67d-459e2aa978a2

📥 Commits

Reviewing files that changed from the base of the PR and between 21d7902 and b638f97.

📒 Files selected for processing (6)
  • fdm-app/app/routes/organization.$slug.$calendar.balance.nitrogen._index.tsx
  • fdm-app/app/routes/organization.$slug.$calendar.balance.organic-matter._index.tsx
  • fdm-calculator/src/balance/nitrogen/index.test.ts
  • fdm-calculator/src/balance/nitrogen/index.ts
  • fdm-calculator/src/balance/organic-matter/index.test.ts
  • fdm-calculator/src/balance/organic-matter/index.ts
✅ Files skipped from review due to trivial changes (3)
  • fdm-calculator/src/balance/nitrogen/index.ts
  • fdm-calculator/src/balance/organic-matter/index.test.ts
  • fdm-calculator/src/balance/organic-matter/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • fdm-calculator/src/balance/nitrogen/index.test.ts

Comment on lines +191 to +202
// Update farmIds selection if it was missing
if (farmIdsSet.size === 0) {
for (const farm of farmsExtended) {
if (farm.hasFields) {
farmIdsSet.add(farm.b_id_farm)
}
}
}

const farmIds = farmsExtended
.filter((farm) => farmIdsSet.has(farm.b_id_farm))
.map((farm) => farm.b_id_farm)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't let the default filter hide the empty-input error.

If every accessible farm is empty, the default-selection path resolves to farmIds = []. The rest of the page then renders with no farmResults, so the nitrogen organization overview looks like a normal calculation instead of exposing the calculator's "No fields in input" state. Please handle the zero-selected-farms case explicitly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fdm-app/app/routes/organization`.$slug.$calendar.balance.nitrogen._index.tsx
around lines 191 - 202, The current default-selection logic can yield farmIds =
[] (if farmIdsSet stays empty), which hides the empty-input state; after
computing farmIds from farmIdsSet/farmsExtended, add an explicit check for
farmIds.length === 0 and handle it by setting/returning the "No fields in input"
error state (or a dedicated flag) so the rendering code that reads farmResults
will show the empty-input UI instead of proceeding with a normal calculation.
Ensure you update the code paths that use farmResults to honor this new
flag/error and reference farmIdsSet, farmsExtended, farmIds and farmResults when
making the change.

Comment on lines +191 to +201
if (farmIdsSet.size === 0) {
for (const farm of farmsExtended) {
if (farm.hasFields) {
farmIdsSet.add(farm.b_id_farm)
}
}
}

const farmIds = farmsExtended
.filter((farm) => farmIdsSet.has(farm.b_id_farm))
.map((farm) => farm.b_id_farm)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle the all-empty organization case explicitly.

When no farmIds query is present and every accessible farm has zero fields, this branch produces farmIds = []. Downstream that means farmResults = [], so the page falls through to a blank farm list and the organization cards/chart never surface the new empty-input error. Please short-circuit this state here, or make the consumer treat the combined result error as authoritative.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@fdm-app/app/routes/organization`.$slug.$calendar.balance.organic-matter._index.tsx
around lines 191 - 201, The current logic yields farmIds = [] when no query is
present and every accessible farm has zero fields, causing downstream
farmResults = [] and hiding the "empty-input" error; detect this case right
after computing farmIdsSet/farmsExtended (when farmIdsSet.size === 0 &&
farmsExtended.length > 0) and short-circuit by returning an explicit error/flag
instead of producing an empty farmIds array—e.g., throw or return a structured
error Response (with a clear message like "organization has no fields") or set
farmIds to a sentinel (null/undefined plus an error flag) so consumers (the code
that reads farmResults) surface the empty-input error instead of rendering a
blank list. Ensure you update the code paths that reference farmIds/farmResults
to handle this error sentinel/Response accordingly.

@SvenVw SvenVw changed the base branch from release/2026-03 to development April 2, 2026 11:34
@SvenVw SvenVw changed the base branch from development to release/2026-03 April 2, 2026 11:35
@SvenVw SvenVw merged commit 38277b8 into release/2026-03 Apr 2, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants