- Rename catalog: ingredient/* → product/* (canonical_name, barcode, nutrition per 100g)
- Rename pantry: product/* → userproduct/* (user-owned items with expiry)
- Squash migrations into single 001_initial_schema.sql (clean-db baseline)
- product_categories: add English canonical name column; fix COALESCE in queries
- Remove product_translations: product names are stored in their original language
- Add default_unit_name to product API responses via unit_translations JOIN
- Add cmd/importoff: bulk import from OpenFoodFacts JSONL dump (COPY + ON CONFLICT)
- Diary: support product_id entries alongside dish_id (CHECK num_nonnulls = 1)
- Home: getLoggedCalories joins both recipes and catalog products
- Flutter: rename models/providers/services to match backend rename
- Flutter: add barcode scan flow for diary (mobile_scanner, product_portion_sheet)
- Flutter: localise 6 new keys across 12 languages (barcode scan, portion weight)
- Routes: GET /products/search, GET /products/barcode/{barcode}, /user-products
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- Rename recognition_jobs → dish_recognition_jobs; add target_date and
target_meal_type columns to capture scan context at submission time
- Add job_id FK on meal_diary so entries are linked to their origin job
- New GET /ai/jobs endpoint returns today's unlinked jobs for the current user
- diary.Entry and CreateRequest gain job_id field; repository reads/writes it
- CORS middleware: allow Accept-Language and Cache-Control headers
- Logging middleware: implement http.Flusher on responseWriter (needed for SSE)
- Consolidate migrations into a single 001_initial_schema.sql
Flutter:
- POST /ai/recognize-dish now sends target_date and target_meal_type
- DishResultSheet accepts jobId; _addToDiary includes it in the diary payload,
saves last-used meal type to SharedPreferences, invalidates todayJobsProvider
- TodayJobsNotifier + todayJobsProvider: loads unlinked jobs via GET /ai/jobs
- Home screen shows _TodayJobsWidget (up to 3 tiles) between macros and meals;
tapping a done tile reopens DishResultSheet with the stored result
- Quick Actions row: third button "История" → /scan/history
- New RecognitionHistoryScreen: full-screen list of today's unlinked jobs
- LocalPreferences wrapper over SharedPreferences (last_used_meal_type)
- app_theme: apply Google Fonts Roboto as default font family
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove denormalized columns (name, calories, protein_g, fat_g, carbs_g)
from meal_diary. Name is now resolved via JOIN with dishes/dish_translations;
macros are computed as recipe.*_per_serving * portions at query time.
- Add dish.Repository.FindOrCreateRecipe: finds or creates a minimal recipe
stub seeded with AI-estimated macros
- recognition/handler: resolve recipe_id synchronously per candidate;
simplify enrichDishInBackground to translations-only
- diary/handler: accept dish_id OR name; always resolve recipe_id via
FindOrCreateRecipe before INSERT
- diary/entity: DishID is now non-nullable string; CreateRequest drops macros
- diary/repository: ListByDate and Create use JOIN to return computed macros
- ai/types: add RecipeID field to DishCandidate
- Update tests and wire_gen accordingly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- Translate all recognition prompts (receipt, products, dish) from Russian to English
- Add lang parameter to Recognizer interface and pass locale.FromContext in handlers
- DishResult type uses candidates array for multi-candidate responses
Client:
- Add meal tracking: diary provider, date selector, meal type model
- DishResult parser: backward-compatible with legacy flat format and new candidates format
- DishResultScreen: sticky bottom button, full-width portion/meal-type inputs,
КБЖУ disclaimer moved under nutrition card, add date field to diary POST body
- Recognition prompts now return dish/product names in user's preferred language
- Onboarding, profile, home screen visual updates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>