Skip to content
5 changes: 5 additions & 0 deletions .changeset/long-pianos-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@svenvw/fdm-app": patch
---

While adding a harvest on the crop rotation table, the harvest date is now validated against the latest sowing date and the earliest cultivation ending date before submitting the form.
106 changes: 53 additions & 53 deletions fdm-app/app/components/blocks/harvest/schema.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import { format } from "date-fns"
import { nl } from "date-fns/locale"
import { z } from "zod"

export const FormSchema = z
.object({
b_lu_harvest_date: z
.string({
required_error:
"Geef een datum op voor wanneer dit gewas is geoogst",
invalid_type_error:
"Geef een datum op voor wanneer dit gewas is geoogst",
required_error: "Selecteer een oogstdatum",
invalid_type_error: "Selecteer een geldige oogstdatum",
})
.transform((val, ctx) => {
const date = new Date(val)
if (Number.isNaN(date.getTime())) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message:
"Geef een datum op voor wanneer dit gewas is geoogst",
message: "Selecteer een geldige oogstdatum",
})
return z.NEVER
}
Expand All @@ -25,182 +24,184 @@ export const FormSchema = z
(val) => (val === "" ? undefined : val),
z.coerce
.number({
invalid_type_error: "Hoeveelheid moet een getal zijn",
invalid_type_error: "De waarde moet een getal zijn",
})
.positive({
message: "Hoeveelheid moet groter zijn dan 0",
message: "De waarde moet groter zijn dan 0",
})
.finite({
message: "Hoeveelheid moet een geheel getal zijn",
message: "De waarde moet een geldig getal zijn",
})
.max(250000, {
message:
"Hoeveelheid mag niet groter zijn dan 250.000 kg DS / ha",
"Opbrengst mag niet groter zijn dan 250.000 kg DS / ha",
})
.safe({
message: "Hoeveelheid moet een safe getal zijn",
message: "De waarde is buiten het toegestane bereik",
})
.optional(),
),
b_lu_yield_fresh: z.preprocess(
(val) => (val === "" ? undefined : val),
z.coerce
.number({
invalid_type_error: "Hoeveelheid moet een getal zijn",
invalid_type_error: "De waarde moet een getal zijn",
})
.positive({
message: "Hoeveelheid moet groter zijn dan 0",
message: "De waarde moet groter zijn dan 0",
})
.finite({
message: "Hoeveelheid moet een geheel getal zijn",
message: "De waarde moet een geldig getal zijn",
})
.max(250000, {
message:
"Hoeveelheid mag niet groter zijn dan 250.000 kg versproduct / ha",
"Opbrengst mag niet groter zijn dan 250.000 kg versproduct / ha",
})
.safe({
message: "Hoeveelheid moet een safe getal zijn",
message: "De waarde is buiten het toegestane bereik",
})
.optional(),
),
b_lu_yield_bruto: z.preprocess(
(val) => (val === "" ? undefined : val),
z.coerce
.number({
invalid_type_error: "Hoeveelheid moet een getal zijn",
invalid_type_error: "De waarde moet een getal zijn",
})
.positive({
message: "Hoeveelheid moet groter zijn dan 0",
message: "De waarde moet groter zijn dan 0",
})
.finite({
message: "Hoeveelheid moet een geheel getal zijn",
message: "De waarde moet een geldig getal zijn",
})
.max(250000, {
message:
"Hoeveelheid mag niet groter zijn dan 250.000 kg versproduct (incl. tarra) / ha",
"Opbrengst mag niet groter zijn dan 250.000 kg versproduct (incl. tarra) / ha",
})
.safe({
message: "Hoeveelheid moet een safe getal zijn",
message: "De waarde is buiten het toegestane bereik",
})
.optional(),
),
b_lu_dm: z.preprocess(
(val) => (val === "" ? undefined : val),
z.coerce
.number({
invalid_type_error: "Hoeveelheid moet een getal zijn",
invalid_type_error: "De waarde moet een getal zijn",
})
.positive({
message: "Hoeveelheid moet groter zijn dan 0",
message: "De waarde moet groter zijn dan 0",
})
.finite({
message: "Hoeveelheid moet een geheel getal zijn",
message: "De waarde moet een geldig getal zijn",
})
.max(1000, {
message:
"Hoeveelheid mag niet groter zijn dan 1.000 g Ds / kg versproduct",
"Het droge stof gehalte mag niet groter zijn dan 1.000 g / kg",
})
.safe({
message: "Hoeveelheid moet een safe getal zijn",
message: "De waarde is buiten het toegestane bereik",
})
.optional(),
),
b_lu_n_harvestable: z.preprocess(
(val) => (val === "" ? undefined : val),
z.coerce
.number({
invalid_type_error: "Hoeveelheid moet een getal zijn",
invalid_type_error: "De waarde moet een getal zijn",
})
.positive({
message: "Hoeveelheid moet groter zijn dan 0",
message: "De waarde moet groter zijn dan 0",
})
.max(1000, {
message: "Hoeveelheid mag niet groter zijn dan 1000",
message:
"De stikstofopbrengst mag niet groter zijn dan 1.000 kg N / ha",
})
.optional(),
),
b_lu_tarra: z.preprocess(
(val) => (val === "" ? undefined : val),
z.coerce
.number({
invalid_type_error: "Hoeveelheid moet een getal zijn",
invalid_type_error: "De waarde moet een getal zijn",
})
.positive({
message: "Hoeveelheid moet groter zijn dan 0",
message: "De waarde moet groter zijn dan 0",
})
.finite({
message: "Hoeveelheid moet een geheel getal zijn",
message: "De waarde moet een geldig getal zijn",
})
.max(25, {
message: "Hoeveelheid mag niet groter zijn dan 25 %",
message: "Het tarra-percentage mag niet hoger zijn dan 25%",
})
.safe({
message: "Hoeveelheid moet een safe getal zijn",
message: "De waarde is buiten het toegestane bereik",
})
.optional(),
),
b_lu_uww: z.preprocess(
(val) => (val === "" ? undefined : val),
z.coerce
.number({
invalid_type_error: "Hoeveelheid moet een getal zijn",
invalid_type_error: "De waarde moet een getal zijn",
})
.positive({
message: "Hoeveelheid moet groter zijn dan 0",
message: "De waarde moet groter zijn dan 0",
})
.finite({
message: "Hoeveelheid moet een geheel getal zijn",
message: "De waarde moet een geldig getal zijn",
})
.min(100, {
message: "Hoeveelheid mag niet kleiner zijn dan 100",
message:
"Het onderwatergewicht mag niet kleiner zijn dan 100 g / 5 kg",
})
.max(1000, {
message:
"Hoeveelheid mag niet groter zijn dan 1.000 g / 5 kg",
"Het onderwatergewicht mag niet groter zijn dan 1.000 g / 5 kg",
})
.safe({
message: "Hoeveelheid moet een safe getal zijn",
message: "De waarde is buiten het toegestane bereik",
})
.optional(),
),
b_lu_moist: z.preprocess(
(val) => (val === "" ? undefined : val),
z.coerce
.number({
invalid_type_error: "Hoeveelheid moet een getal zijn",
invalid_type_error: "De waarde moet een getal zijn",
})
.positive({
message: "Hoeveelheid moet groter zijn dan 0",
message: "De waarde moet groter zijn dan 0",
})
.finite({
message: "Hoeveelheid moet een geheel getal zijn",
message: "De waarde moet een geldig getal zijn",
})
.max(100, {
message: "Hoeveelheid mag niet groter zijn dan 100 %",
message: "Het vochtpercentage mag niet hoger zijn dan 100%",
})
.safe({
message: "Hoeveelheid moet een safe getal zijn",
message: "De waarde is buiten het toegestane bereik",
})
.optional(),
),
b_lu_cp: z.preprocess(
(val) => (val === "" ? undefined : val),
z.coerce
.number({
invalid_type_error: "Hoeveelheid moet een getal zijn",
invalid_type_error: "De waarde moet een getal zijn",
})
.positive({
message: "Hoeveelheid moet groter zijn dan 0",
message: "De waarde moet groter zijn dan 0",
})
.finite({
message: "Hoeveelheid moet een geheel getal zijn",
message: "De waarde moet een geldig getal zijn",
})
.max(500, {
message:
"Hoeveelheid mag niet groter zijn dan 500 g RE / kg DS",
"Het ruw eiwit gehalte mag niet groter zijn dan 500 g / kg DS",
})
.safe({
message: "Hoeveelheid moet een safe getal zijn",
message: "De waarde is buiten het toegestane bereik",
})
.optional(),
),
Expand All @@ -216,7 +217,7 @@ export const FormSchema = z
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Oogstdatum moet na de zaaidatum van de teelt liggen",
message: `De oogstdatum mag niet vóór de start van de teelt (${format(data.b_lu_start, "PP", { locale: nl })}) vallen`,
path: ["b_lu_harvest_date"],
})
}
Expand All @@ -228,8 +229,7 @@ export const FormSchema = z
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message:
"Oogstdatum mag niet na de einddatum van de teelt liggen",
message: `De oogstdatum mag niet ná het einde van de teelt (${format(data.b_lu_end, "PP", { locale: nl })}) vallen`,
path: ["b_lu_harvest_date"],
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,29 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
)
}

const b_lu_starts = selectedFields.map(
(field) =>
field.cultivations.find((cultivation) =>
cultivationIds.includes(cultivation.b_lu_catalogue),
)?.b_lu_start,
)
const b_lu_ends = selectedFields.map(
(field) =>
field.cultivations.find((cultivation) =>
cultivationIds.includes(cultivation.b_lu_catalogue),
)?.b_lu_end,
)
const b_lu_start = b_lu_starts.reduce(
(max, date) =>
max && date ? (max > date ? max : date) : max || date,
undefined,
)
const b_lu_end = b_lu_ends.reduce(
(min, date) =>
min && date ? (min < date ? min : date) : min || date,
undefined,
)

const fieldOptions = allFieldsWithCultivations.map((field) => {
if (!field?.b_id || !field?.b_name) {
throw new Error("Invalid field data structure")
Expand Down Expand Up @@ -327,6 +350,8 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
harvestApplication: harvestApplication,
harvestableAnalysis: harvestableAnalysis,
harvestParameters: harvestParameters,
b_lu_start: b_lu_start,
b_lu_end: b_lu_end,
create: url.searchParams.has("create"),
}
} catch (error) {
Expand Down Expand Up @@ -686,6 +711,7 @@ export default function FarmRotationHarvestAddIndex() {
</div>
) : loaderData.fieldAmount > 0 ? (
<HarvestForm
key={selectedFieldIds.join(",")}
harvestParameters={
loaderData.harvestParameters
}
Expand Down Expand Up @@ -733,13 +759,8 @@ export default function FarmRotationHarvestAddIndex() {
loaderData.cultivation
.b_lu_harvestable
}
b_lu_start={
loaderData.cultivation
.b_lu_start
}
b_lu_end={
loaderData.cultivation.b_lu_end
}
b_lu_start={loaderData.b_lu_start}
b_lu_end={loaderData.b_lu_end}
action={modifySearchParams(
`${location.pathname}${location.search}`,
(searchParams) =>
Expand Down