Bring all areas to 60%: modules, reporting, i18n, views, data

Business modules deepened:
- sale: tag_ids, invoice/delivery counts with computes
- stock: _action_confirm/_action_done on stock.move, quant update stub
- purchase: done state added
- hr: parent_id, address_home_id, no_of_recruitment
- project: user_id, date_start, kanban_state on tasks

Reporting framework (0% → 60%):
- ir.actions.report model registered
- /report/html/<name>/<ids> endpoint serves styled HTML reports
- Report-to-model mapping for invoice, sale, stock, purchase

i18n framework (0% → 60%):
- ir.translation model with src/value/lang/type fields
- handleTranslations loads from DB, returns per-module structure
- 140 German translations seeded (UI terms across all modules)
- res_lang seeded with en_US + de_DE

Views/UI improved:
- Stored form views: sale.order (with editable O2M lines), account.move
  (with Post/Cancel buttons), res.partner (with title)
- Stored list views: purchase.order, hr.employee, project.project

Demo data expanded:
- 5 products (templates + variants with codes)
- 3 HR departments, 3 employees
- 2 projects

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc
2026-04-02 20:11:45 +02:00
parent eb92a2e239
commit 03fd58a852
13 changed files with 944 additions and 31 deletions

View File

@@ -2,6 +2,7 @@ package server
import (
"encoding/json"
"fmt"
"net/http"
)
@@ -46,21 +47,64 @@ func (s *Server) handleManifest(w http.ResponseWriter, r *http.Request) {
})
}
// handleBootstrapTranslations returns empty translations for initial boot.
// handleBootstrapTranslations returns translations for initial boot (login page etc.).
// Mirrors: odoo/addons/web/controllers/webclient.py bootstrap_translations()
func (s *Server) handleBootstrapTranslations(w http.ResponseWriter, r *http.Request) {
lang := "en_US"
// Try to detect language from request
if r.Method == http.MethodPost {
var req JSONRPCRequest
if err := json.NewDecoder(r.Body).Decode(&req); err == nil {
var params struct {
Mods []string `json:"mods"`
Lang string `json:"lang"`
}
if err := json.Unmarshal(req.Params, &params); err == nil && params.Lang != "" {
lang = params.Lang
}
}
}
langParams := map[string]interface{}{
"direction": "ltr",
"date_format": "%%m/%%d/%%Y",
"time_format": "%%H:%%M:%%S",
"grouping": "[3,0]",
"decimal_point": ".",
"thousands_sep": ",",
"week_start": 1,
}
// Try to load language parameters from res_lang
var dateFormat, timeFormat, decimalPoint, thousandsSep, direction string
err := s.pool.QueryRow(r.Context(),
`SELECT date_format, time_format, decimal_point, thousands_sep, direction
FROM res_lang WHERE code = $1 AND active = true`, lang,
).Scan(&dateFormat, &timeFormat, &decimalPoint, &thousandsSep, &direction)
if err == nil {
langParams["date_format"] = dateFormat
langParams["time_format"] = timeFormat
langParams["decimal_point"] = decimalPoint
langParams["thousands_sep"] = thousandsSep
langParams["direction"] = direction
}
// Check if multiple languages are active
multiLang := false
var langCount int
if err := s.pool.QueryRow(r.Context(),
`SELECT COUNT(*) FROM res_lang WHERE active = true`).Scan(&langCount); err == nil {
if langCount > 1 {
multiLang = true
}
}
s.writeJSONRPC(w, nil, map[string]interface{}{
"lang": "en_US",
"hash": "empty",
"lang_parameters": map[string]interface{}{
"direction": "ltr",
"date_format": "%%m/%%d/%%Y",
"time_format": "%%H:%%M:%%S",
"grouping": "[3,0]",
"decimal_point": ".",
"thousands_sep": ",",
"week_start": 1,
},
"modules": map[string]interface{}{},
"multi_lang": false,
"lang": lang,
"hash": fmt.Sprintf("odoo-go-boot-%s", lang),
"lang_parameters": langParams,
"modules": map[string]interface{}{},
"multi_lang": multiLang,
}, nil)
}