Files
goodie/addons/purchase/models/purchase_order.go
Marc 0ed29fe2fd Odoo ERP ported to Go — complete backend + original OWL frontend
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>
2026-03-31 01:45:09 +02:00

178 lines
5.5 KiB
Go

package models
import "odoo-go/pkg/orm"
// initPurchaseOrder registers purchase.order and purchase.order.line.
// Mirrors: odoo/addons/purchase/models/purchase_order.py
func initPurchaseOrder() {
// purchase.order — the purchase order header
m := orm.NewModel("purchase.order", orm.ModelOpts{
Description: "Purchase Order",
Order: "priority desc, id desc",
RecName: "name",
})
// -- Identity --
m.AddFields(
orm.Char("name", orm.FieldOpts{
String: "Order Reference", Required: true, Index: true, Readonly: true, Default: "New",
}),
orm.Selection("state", []orm.SelectionItem{
{Value: "draft", Label: "RFQ"},
{Value: "sent", Label: "RFQ Sent"},
{Value: "to approve", Label: "To Approve"},
{Value: "purchase", Label: "Purchase Order"},
{Value: "cancel", Label: "Cancelled"},
}, orm.FieldOpts{String: "Status", Default: "draft", Readonly: true, Index: true}),
orm.Selection("priority", []orm.SelectionItem{
{Value: "0", Label: "Normal"},
{Value: "1", Label: "Urgent"},
}, orm.FieldOpts{String: "Priority", Default: "0", Index: true}),
)
// -- Partner & Dates --
m.AddFields(
orm.Many2one("partner_id", "res.partner", orm.FieldOpts{
String: "Vendor", Required: true, Index: true,
}),
orm.Datetime("date_order", orm.FieldOpts{
String: "Order Deadline", Required: true, Index: true,
}),
orm.Datetime("date_planned", orm.FieldOpts{
String: "Expected Arrival",
}),
orm.Datetime("date_approve", orm.FieldOpts{
String: "Confirmation Date", Readonly: true, Index: true,
}),
)
// -- Company & Currency --
m.AddFields(
orm.Many2one("company_id", "res.company", orm.FieldOpts{
String: "Company", Required: true, Index: true,
}),
orm.Many2one("currency_id", "res.currency", orm.FieldOpts{
String: "Currency", Required: true,
}),
)
// -- Lines --
m.AddFields(
orm.One2many("order_line", "purchase.order.line", "order_id", orm.FieldOpts{
String: "Order Lines",
}),
)
// -- Amounts --
m.AddFields(
orm.Monetary("amount_untaxed", orm.FieldOpts{
String: "Untaxed Amount", Compute: "_compute_amount", Store: true, CurrencyField: "currency_id",
}),
orm.Monetary("amount_tax", orm.FieldOpts{
String: "Taxes", Compute: "_compute_amount", Store: true, CurrencyField: "currency_id",
}),
orm.Monetary("amount_total", orm.FieldOpts{
String: "Total", Compute: "_compute_amount", Store: true, CurrencyField: "currency_id",
}),
)
// -- Invoice Status --
m.AddFields(
orm.Selection("invoice_status", []orm.SelectionItem{
{Value: "no", Label: "Nothing to Bill"},
{Value: "to invoice", Label: "Waiting Bills"},
{Value: "invoiced", Label: "Fully Billed"},
}, orm.FieldOpts{String: "Billing Status", Compute: "_compute_invoice_status", Store: true}),
)
// -- Accounting --
m.AddFields(
orm.Many2one("fiscal_position_id", "account.fiscal.position", orm.FieldOpts{
String: "Fiscal Position",
}),
orm.Many2one("payment_term_id", "account.payment.term", orm.FieldOpts{
String: "Payment Terms",
}),
)
// -- Notes --
m.AddFields(
orm.Text("notes", orm.FieldOpts{String: "Terms and Conditions"}),
orm.Char("origin", orm.FieldOpts{String: "Source Document"}),
)
// purchase.order.line — individual line items on a PO
initPurchaseOrderLine()
}
// initPurchaseOrderLine registers purchase.order.line.
// Mirrors: odoo/addons/purchase/models/purchase_order_line.py
func initPurchaseOrderLine() {
m := orm.NewModel("purchase.order.line", orm.ModelOpts{
Description: "Purchase Order Line",
Order: "order_id, sequence, id",
})
// -- Parent --
m.AddFields(
orm.Many2one("order_id", "purchase.order", orm.FieldOpts{
String: "Order Reference", Required: true, OnDelete: orm.OnDeleteCascade, Index: true,
}),
orm.Integer("sequence", orm.FieldOpts{String: "Sequence", Default: 10}),
)
// -- Product --
m.AddFields(
orm.Many2one("product_id", "product.product", orm.FieldOpts{
String: "Product", Index: true,
}),
orm.Char("name", orm.FieldOpts{String: "Description", Required: true}),
orm.Float("product_qty", orm.FieldOpts{String: "Quantity", Required: true, Default: 1.0}),
orm.Many2one("product_uom", "uom.uom", orm.FieldOpts{
String: "Unit of Measure", Required: true,
}),
)
// -- Pricing --
m.AddFields(
orm.Float("price_unit", orm.FieldOpts{String: "Unit Price", Required: true}),
orm.Many2many("tax_ids", "account.tax", orm.FieldOpts{String: "Taxes"}),
orm.Float("discount", orm.FieldOpts{String: "Discount (%)", Default: 0.0}),
orm.Monetary("price_subtotal", orm.FieldOpts{
String: "Subtotal", Compute: "_compute_amount", Store: true, CurrencyField: "currency_id",
}),
orm.Monetary("price_total", orm.FieldOpts{
String: "Total", Compute: "_compute_amount", Store: true, CurrencyField: "currency_id",
}),
orm.Many2one("currency_id", "res.currency", orm.FieldOpts{
String: "Currency",
}),
)
// -- Dates --
m.AddFields(
orm.Datetime("date_planned", orm.FieldOpts{String: "Expected Arrival"}),
)
// -- Quantities --
m.AddFields(
orm.Float("qty_received", orm.FieldOpts{
String: "Received Qty", Compute: "_compute_qty_received", Store: true,
}),
orm.Float("qty_invoiced", orm.FieldOpts{
String: "Billed Qty", Compute: "_compute_qty_invoiced", Store: true,
}),
orm.Float("qty_to_invoice", orm.FieldOpts{
String: "To Invoice Quantity", Compute: "_compute_qty_to_invoice", Store: true,
}),
)
// -- Company --
m.AddFields(
orm.Many2one("company_id", "res.company", orm.FieldOpts{
String: "Company", Index: true,
}),
)
}