Full port of Odoo's ERP system from Python to Go, with the original Odoo JavaScript frontend (OWL framework) running against the Go server. Backend (10,691 LoC Go): - Custom ORM: CRUD, domains→SQL with JOINs, computed fields, sequences - 93 models across 14 modules (base, account, sale, stock, purchase, hr, project, crm, fleet, product, l10n_de, google_address/translate/calendar) - Auth with bcrypt + session cookies - Setup wizard (company, SKR03 chart, admin, demo data) - Double-entry bookkeeping constraint - Sale→Invoice workflow (confirm SO → generate invoice → post) - SKR03 chart of accounts (110 accounts) + German taxes (USt/VSt) - Record rules (multi-company filter) - Google integrations as opt-in modules (Maps, Translate, Calendar) Frontend: - Odoo's original OWL webclient (503 JS modules, 378 XML templates) - JS transpiled via Odoo's js_transpiler (ES modules → odoo.define) - SCSS compiled to CSS (675KB) via dart-sass - XML templates compiled to registerTemplate() JS calls - Static file serving from Odoo source addons - Login page, session management, menu navigation - Contacts list view renders with real data from PostgreSQL Infrastructure: - 14MB single binary (CGO_ENABLED=0) - Docker Compose (Go server + PostgreSQL 16) - Zero phone-home (no outbound calls to odoo.com) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
80 lines
3.1 KiB
Go
80 lines
3.1 KiB
Go
package models
|
|
|
|
import "odoo-go/pkg/orm"
|
|
|
|
// initResCurrency registers currency models.
|
|
// Mirrors: odoo/addons/base/models/res_currency.py
|
|
|
|
func initResCurrency() {
|
|
// res.currency — Currency definition
|
|
m := orm.NewModel("res.currency", orm.ModelOpts{
|
|
Description: "Currency",
|
|
Order: "name",
|
|
RecName: "name",
|
|
})
|
|
|
|
m.AddFields(
|
|
orm.Char("name", orm.FieldOpts{String: "Currency", Required: true, Size: 3}),
|
|
orm.Char("full_name", orm.FieldOpts{String: "Name"}),
|
|
orm.Char("symbol", orm.FieldOpts{String: "Symbol", Required: true, Size: 4}),
|
|
orm.Integer("decimal_places", orm.FieldOpts{String: "Decimal Places"}),
|
|
orm.Float("rounding", orm.FieldOpts{String: "Rounding Factor"}),
|
|
orm.Boolean("active", orm.FieldOpts{String: "Active", Default: true}),
|
|
orm.Selection("position", []orm.SelectionItem{
|
|
{Value: "after", Label: "After Amount"},
|
|
{Value: "before", Label: "Before Amount"},
|
|
}, orm.FieldOpts{String: "Symbol Position", Default: "after"}),
|
|
orm.Float("rate", orm.FieldOpts{String: "Current Rate", Compute: "_compute_current_rate"}),
|
|
orm.One2many("rate_ids", "res.currency.rate", "currency_id", orm.FieldOpts{String: "Rates"}),
|
|
)
|
|
|
|
// res.currency.rate — Exchange rates
|
|
rate := orm.NewModel("res.currency.rate", orm.ModelOpts{
|
|
Description: "Currency Rate",
|
|
Order: "name desc",
|
|
RecName: "name",
|
|
})
|
|
|
|
rate.AddFields(
|
|
orm.Date("name", orm.FieldOpts{String: "Date", Required: true, Index: true, Default: "today"}),
|
|
orm.Float("rate", orm.FieldOpts{String: "Rate", Required: true}),
|
|
orm.Float("inverse_company_rate", orm.FieldOpts{String: "Inverse Rate"}),
|
|
orm.Many2one("currency_id", "res.currency", orm.FieldOpts{
|
|
String: "Currency", Required: true, OnDelete: orm.OnDeleteCascade,
|
|
}),
|
|
orm.Many2one("company_id", "res.company", orm.FieldOpts{String: "Company"}),
|
|
)
|
|
|
|
// res.country — Country
|
|
// Mirrors: odoo/addons/base/models/res_country.py
|
|
country := orm.NewModel("res.country", orm.ModelOpts{
|
|
Description: "Country",
|
|
Order: "name",
|
|
})
|
|
|
|
country.AddFields(
|
|
orm.Char("name", orm.FieldOpts{String: "Country Name", Required: true, Translate: true}),
|
|
orm.Char("code", orm.FieldOpts{String: "Country Code", Size: 2, Required: true}),
|
|
orm.Char("phone_code", orm.FieldOpts{String: "Country Calling Code"}),
|
|
orm.Many2one("currency_id", "res.currency", orm.FieldOpts{String: "Currency"}),
|
|
orm.Binary("image", orm.FieldOpts{String: "Flag"}),
|
|
orm.One2many("state_ids", "res.country.state", "country_id", orm.FieldOpts{String: "States"}),
|
|
orm.Char("address_format", orm.FieldOpts{String: "Layout in Reports"}),
|
|
orm.Char("vat_label", orm.FieldOpts{String: "Vat Label", Translate: true}),
|
|
)
|
|
|
|
// res.country.state — Country state/province
|
|
state := orm.NewModel("res.country.state", orm.ModelOpts{
|
|
Description: "Country state",
|
|
Order: "code",
|
|
})
|
|
|
|
state.AddFields(
|
|
orm.Char("name", orm.FieldOpts{String: "State Name", Required: true}),
|
|
orm.Char("code", orm.FieldOpts{String: "State Code", Required: true, Size: 3}),
|
|
orm.Many2one("country_id", "res.country", orm.FieldOpts{
|
|
String: "Country", Required: true, OnDelete: orm.OnDeleteCascade,
|
|
}),
|
|
)
|
|
}
|