Add backdating support to egg harvest and sell forms

Both forms now have datetime pickers like the feed forms, allowing
users to record events at past timestamps. Each form has a unique
field_id (harvest_datetime, sell_datetime) to avoid conflicts.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-08 09:24:26 +00:00
parent 4b951d428f
commit 29fbe68c73
2 changed files with 29 additions and 4 deletions

View File

@@ -26,6 +26,26 @@ from animaltrack.services.products import ProductService, ValidationError
from animaltrack.web.templates import render_page from animaltrack.web.templates import render_page
from animaltrack.web.templates.eggs import eggs_page from animaltrack.web.templates.eggs import eggs_page
def _parse_ts_utc(form_value: str | None) -> int:
"""Parse ts_utc from form, defaulting to current time if empty or zero.
Args:
form_value: The ts_utc value from form data.
Returns:
Timestamp in milliseconds. Returns current time if form_value is
None, empty, or "0".
"""
if not form_value or form_value == "0":
return int(time.time() * 1000)
try:
ts = int(form_value)
return ts if ts > 0 else int(time.time() * 1000)
except (ValueError, TypeError):
return int(time.time() * 1000)
# APIRouter for multi-file route organization # APIRouter for multi-file route organization
ar = APIRouter() ar = APIRouter()
@@ -147,8 +167,8 @@ async def product_collected(request: Request, session):
request, locations, products, location_id, "Quantity must be at least 1" request, locations, products, location_id, "Quantity must be at least 1"
) )
# Get current timestamp # Get timestamp - use provided or current (supports backdating)
ts_utc = int(time.time() * 1000) ts_utc = _parse_ts_utc(form.get("ts_utc"))
# Resolve ducks at location # Resolve ducks at location
resolved_ids = resolve_ducks_at_location(db, location_id, ts_utc) resolved_ids = resolve_ducks_at_location(db, location_id, ts_utc)
@@ -276,8 +296,8 @@ async def product_sold(request: Request, session):
request, locations, products, product_code, "Total price cannot be negative" request, locations, products, product_code, "Total price cannot be negative"
) )
# Get current timestamp # Get timestamp - use provided or current (supports backdating)
ts_utc = int(time.time() * 1000) ts_utc = _parse_ts_utc(form.get("ts_utc"))
# Create product service # Create product service
event_store = EventStore(db) event_store = EventStore(db)

View File

@@ -16,6 +16,7 @@ from monsterui.all import (
from ulid import ULID from ulid import ULID
from animaltrack.models.reference import Location, Product from animaltrack.models.reference import Location, Product
from animaltrack.web.templates.actions import event_datetime_field
def eggs_page( def eggs_page(
@@ -163,6 +164,8 @@ def harvest_form(
name="notes", name="notes",
placeholder="Optional notes", placeholder="Optional notes",
), ),
# Event datetime picker (for backdating)
event_datetime_field("harvest_datetime"),
# Hidden nonce for idempotency # Hidden nonce for idempotency
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button
@@ -263,6 +266,8 @@ def sell_form(
name="notes", name="notes",
placeholder="Optional notes", placeholder="Optional notes",
), ),
# Event datetime picker (for backdating)
event_datetime_field("sell_datetime"),
# Hidden nonce for idempotency # Hidden nonce for idempotency
Hidden(name="nonce", value=str(ULID())), Hidden(name="nonce", value=str(ULID())),
# Submit button # Submit button