- Add units + unit_translations tables with FK constraints on products and ingredient_mappings - Normalize products.unit from Russian strings (г, кг) to English codes (g, kg) - Load units at startup (in-memory registry) and serve via GET /units (language-aware) - Replace hardcoded _units lists and _mapUnit() functions in Flutter with unitsProvider FutureProvider - Re-fetches automatically when language changes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
74 lines
1.7 KiB
Go
74 lines
1.7 KiB
Go
package units
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
// Record is a unit loaded from DB with all its translations.
|
|
type Record struct {
|
|
Code string
|
|
SortOrder int
|
|
Translations map[string]string // lang → localized name
|
|
}
|
|
|
|
// Records is the ordered list of active units, populated by LoadFromDB at startup.
|
|
var Records []Record
|
|
|
|
// LoadFromDB queries units + unit_translations and populates Records.
|
|
func LoadFromDB(ctx context.Context, pool *pgxpool.Pool) error {
|
|
rows, err := pool.Query(ctx, `
|
|
SELECT u.code, u.sort_order, ut.lang, ut.name
|
|
FROM units u
|
|
LEFT JOIN unit_translations ut ON ut.unit_code = u.code
|
|
ORDER BY u.sort_order, ut.lang`)
|
|
if err != nil {
|
|
return fmt.Errorf("load units from db: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
byCode := map[string]*Record{}
|
|
var order []string
|
|
for rows.Next() {
|
|
var code string
|
|
var sortOrder int
|
|
var lang, name *string
|
|
if err := rows.Scan(&code, &sortOrder, &lang, &name); err != nil {
|
|
return err
|
|
}
|
|
if _, ok := byCode[code]; !ok {
|
|
byCode[code] = &Record{Code: code, SortOrder: sortOrder, Translations: map[string]string{}}
|
|
order = append(order, code)
|
|
}
|
|
if lang != nil && name != nil {
|
|
byCode[code].Translations[*lang] = *name
|
|
}
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return err
|
|
}
|
|
|
|
result := make([]Record, 0, len(order))
|
|
for _, code := range order {
|
|
result = append(result, *byCode[code])
|
|
}
|
|
Records = result
|
|
return nil
|
|
}
|
|
|
|
// NameFor returns the localized name for a unit code.
|
|
// Falls back to the code itself when no translation exists.
|
|
func NameFor(code, lang string) string {
|
|
for _, u := range Records {
|
|
if u.Code == code {
|
|
if name, ok := u.Translations[lang]; ok {
|
|
return name
|
|
}
|
|
return u.Code
|
|
}
|
|
}
|
|
return code
|
|
}
|