From eee85523457e3f5ba2f3880ce20eed4f29a286be Mon Sep 17 00:00:00 2001 From: Petru Paler Date: Fri, 9 Jan 2026 06:37:31 +0000 Subject: [PATCH] Allow recording zero eggs collected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable recording "checked coop, found 0 eggs" to distinguish from days when the coop wasn't checked at all. Statistics remain eggs/calendar day. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/animaltrack/events/payloads.py | 2 +- src/animaltrack/web/routes/eggs.py | 4 ++-- src/animaltrack/web/templates/eggs.py | 4 ++-- tests/test_event_payloads.py | 18 +++++++++++++++--- tests/test_web_eggs.py | 18 ++++++++++++++---- 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/animaltrack/events/payloads.py b/src/animaltrack/events/payloads.py index 0b947e8..1337ce0 100644 --- a/src/animaltrack/events/payloads.py +++ b/src/animaltrack/events/payloads.py @@ -308,7 +308,7 @@ class ProductCollectedPayload(BaseModel): location_id: str = Field(..., min_length=26, max_length=26) product_code: str - quantity: int = Field(..., ge=1) + quantity: int = Field(..., ge=0) # 0 allowed: checked but found none resolved_ids: list[str] = Field(..., min_length=1) notes: str | None = None diff --git a/src/animaltrack/web/routes/eggs.py b/src/animaltrack/web/routes/eggs.py index 5b40816..5e42a90 100644 --- a/src/animaltrack/web/routes/eggs.py +++ b/src/animaltrack/web/routes/eggs.py @@ -376,9 +376,9 @@ async def product_collected(request: Request, session): request, db, locations, products, location_id, "Quantity must be a number" ) - if quantity < 1: + if quantity < 0: return _render_harvest_error( - request, db, locations, products, location_id, "Quantity must be at least 1" + request, db, locations, products, location_id, "Quantity cannot be negative" ) # Get timestamp - use provided or current (supports backdating) diff --git a/src/animaltrack/web/templates/eggs.py b/src/animaltrack/web/templates/eggs.py index 6a37bbd..5b78493 100644 --- a/src/animaltrack/web/templates/eggs.py +++ b/src/animaltrack/web/templates/eggs.py @@ -184,13 +184,13 @@ def harvest_form( id="location_id", name="location_id", ), - # Quantity input (integer only, min=1) + # Quantity input (integer only, 0 allowed for "checked but found none") LabelInput( "Quantity", id="quantity", name="quantity", type="number", - min="1", + min="0", step="1", placeholder="Number of eggs", required=True, diff --git a/tests/test_event_payloads.py b/tests/test_event_payloads.py index 17e6dc3..66bc3d8 100644 --- a/tests/test_event_payloads.py +++ b/tests/test_event_payloads.py @@ -285,15 +285,27 @@ class TestProductPayloads: ) assert payload.quantity == 12 - def test_quantity_must_be_positive(self): - """quantity must be >= 1.""" + def test_quantity_zero_is_valid(self): + """quantity=0 is valid (checked but found none).""" + from animaltrack.events.payloads import ProductCollectedPayload + + payload = ProductCollectedPayload( + location_id="01ARZ3NDEKTSV4RRFFQ69G5FAV", + product_code="egg.duck", + quantity=0, + resolved_ids=["01ARZ3NDEKTSV4RRFFQ69G5FAV"], + ) + assert payload.quantity == 0 + + def test_quantity_cannot_be_negative(self): + """quantity must be >= 0.""" from animaltrack.events.payloads import ProductCollectedPayload with pytest.raises(ValidationError): ProductCollectedPayload( location_id="01ARZ3NDEKTSV4RRFFQ69G5FAV", product_code="egg.duck", - quantity=0, + quantity=-1, resolved_ids=["01ARZ3NDEKTSV4RRFFQ69G5FAV"], ) diff --git a/tests/test_web_eggs.py b/tests/test_web_eggs.py index f8fec25..f867012 100644 --- a/tests/test_web_eggs.py +++ b/tests/test_web_eggs.py @@ -137,10 +137,10 @@ class TestEggCollection: assert event_row is not None assert event_row[0] == "ProductCollected" - def test_egg_collection_validation_quantity_zero( - self, client, location_strip1_id, ducks_at_strip1 + def test_egg_collection_quantity_zero_accepted( + self, client, seeded_db, location_strip1_id, ducks_at_strip1 ): - """quantity=0 returns 422.""" + """quantity=0 is accepted (checked coop, found no eggs).""" resp = client.post( "/actions/product-collected", data={ @@ -150,7 +150,17 @@ class TestEggCollection: }, ) - assert resp.status_code == 422 + assert resp.status_code in [200, 302, 303] + + # Verify event was created with quantity=0 + event_row = seeded_db.execute( + "SELECT payload FROM events WHERE type = 'ProductCollected' ORDER BY id DESC LIMIT 1" + ).fetchone() + assert event_row is not None + import json + + payload = json.loads(event_row[0]) + assert payload["quantity"] == 0 def test_egg_collection_validation_quantity_negative( self, client, location_strip1_id, ducks_at_strip1