LF-5276: Save and return which crops or animals each expense is linked to models endpoints tests#4169
Conversation
…relations and cropVarietiesBelongToFarm Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… allocations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use .length check so PATCH can send farm_expense_animal: [] with farm_expense_crop_variety items to transition between allocation types. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the 404 workaround in the saga now that the API consistently returns 200 with an empty array for farms with no expenses. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…less than value (expense total)
kathyavini
left a comment
There was a problem hiding this comment.
Really nice! The middleware is very well organized and the controller logic looks great. Thank you for handling and thinking of so many required checks -- again I'm surprised by how much can go wrong when multiple farm entities are involved 😅
In testing in Postman I (accidentally 😬) found two points in the controllers that cause a full API crash; neither is from the new code and hopefully those are the only ones!
There was a problem hiding this comment.
Not from this PR, but could this be moved inside the try/catch?
I crashed my backend when testing in Postman sending a non-existent expense_type_id; didn't expect that to bring the whole API down 😝
Additionally (for future safety) should baseController.isDeleted also return record?.deleted? Caller is not really set up to handle that case though (not deleted but not found)...
| const isAddingAnimalExpense = !!farm_expense_animal?.length; | ||
| const isAddingCropVarietyExpense = !!farm_expense_crop_variety?.length; | ||
|
|
||
| if ((isNewExpense || !newValue) && !isAddingAnimalExpense && !isAddingCropVarietyExpense) { |
There was a problem hiding this comment.
Since values can be 0, I think it will have to be newValue === undefined rather than !newValue to avoid early return on checking the allocations for a value that's been updated to 0.
|
|
||
| interface AnimalExpenseItem { | ||
| id?: number; | ||
| farm_expense_id: number; |
There was a problem hiding this comment.
farm_expense_id is a uuid, but it looks like it's number in three of these types (and string in the 4th!)
|
|
||
| interface CropVarietyExpenseItem { | ||
| farm_expense_id: number; | ||
| crop_variety_id: string | null; |
There was a problem hiding this comment.
Can the crop_variety_id ever be null?
There was a problem hiding this comment.
No 😅 Thank you! 🙏
| @@ -30,7 +32,9 @@ const farmExpenseController = { | |||
There was a problem hiding this comment.
Not this PR, but this caused a server crash in my testing! This needs a return before the res.status or sending an object will crash with a
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I think the controller definitely needs fixing there anyway, but I wonder why Claude went with a more permissive pattern in the middleware that allows non-arrays, do you know? 🤔
// checkFarmExpense.ts
const expenses = Array.isArray(body) ? body : [body];There was a problem hiding this comment.
The request body is an array in POST, but an object in PATCH.
I initially wondered that too 😂
| const isAddingCropVarietyExpense = !!farm_expense_crop_variety?.length; | ||
| const isAddingAnimalExpense = !!farm_expense_animal?.length; | ||
|
|
||
| if (farm_expense_crop_variety !== undefined || isAddingAnimalExpense) { |
There was a problem hiding this comment.
I didn't understand the conditionals at first glance, but this is very clever! ❤️
|
|
||
| try { | ||
| ({ animalIds, batchIds } = getUniqueAnimalAndBatchIdsFromAnimalSale(animal_sale)); | ||
| ({ animalIds, batchIds } = getUniqueAnimalAndBatchIds(animal_sale)); |
There was a problem hiding this comment.
It's just for the error message, but looks like it needs the new 2nd parameter entityName here too
| return res.status(400).send('value is required when creating a farm expense'); | ||
| } | ||
|
|
||
| if (farm_expense_animal?.length) { |
There was a problem hiding this comment.
I think an array check will be needed here as well. insertGraph is happy to accept a single object as well as array, but since length is falsy the check won't run. So sending an object gets around the farm ownership check:
"farm_expense_animal": {
"animal_id": 45, // another farm's animal ID
"allocated_value": 150
}| }); | ||
|
|
||
| test('Returns 400 if value is missing', async () => { | ||
| const { value: _value, ...expenseWithoutValue } = mocks.fakeExpense({ |
There was a problem hiding this comment.
Did you get an unused variable linting error here? For me it's fine even without the rename to _value 🤔
There was a problem hiding this comment.
AI did it, so I didn't check, but I'm not getting an error either. I guess I would if it were TypeScript though 🤔
SayakaOno
left a comment
There was a problem hiding this comment.
Thank you so much for catching all those errors!! I think I’ve addressed them all.
Could you please re-review? Thank you 🙏
| @@ -30,7 +32,9 @@ const farmExpenseController = { | |||
There was a problem hiding this comment.
The request body is an array in POST, but an object in PATCH.
I initially wondered that too 😂
|
|
||
| interface CropVarietyExpenseItem { | ||
| farm_expense_id: number; | ||
| crop_variety_id: string | null; |
There was a problem hiding this comment.
No 😅 Thank you! 🙏
| return res.status(400).send('value is required when creating a farm expense'); | ||
| } | ||
|
|
||
| if (farm_expense_animal?.length) { |
| }); | ||
|
|
||
| test('Returns 400 if value is missing', async () => { | ||
| const { value: _value, ...expenseWithoutValue } = mocks.fakeExpense({ |
There was a problem hiding this comment.
AI did it, so I didn't check, but I'm not getting an error either. I guess I would if it were TypeScript though 🤔
Description
farmExpenseAnimalModelandfarmExpenseCropVarietyModelfarmExpenserelationscheckFarmExpensemiddleware for validationcropVarietiesBelongToFarmcheckSalefarmExpenseControllerto handle allocation modifications[]instead of404when there are no expensesPATCH sample requests:
farm_expense_crop_varietyJira link: https://lite-farm.atlassian.net/browse/LF-5276
Type of change
How Has This Been Tested?
Checklist:
pnpm i18nto help with this)