package models import "odoo-go/pkg/orm" // initAccountRecurring registers account.move.recurring — recurring entry templates. // Mirrors: odoo/addons/account/models/account_move.py (recurring entries feature) // // Allows defining templates that automatically generate journal entries // on a schedule (daily, weekly, monthly, quarterly, yearly). func initAccountRecurring() { m := orm.NewModel("account.move.recurring", orm.ModelOpts{ Description: "Recurring Entry", Order: "name", }) m.AddFields( orm.Char("name", orm.FieldOpts{String: "Name", Required: true}), orm.Many2one("journal_id", "account.journal", orm.FieldOpts{String: "Journal", Required: true}), orm.Many2one("company_id", "res.company", orm.FieldOpts{String: "Company"}), orm.Selection("period", []orm.SelectionItem{ {Value: "daily", Label: "Daily"}, {Value: "weekly", Label: "Weekly"}, {Value: "monthly", Label: "Monthly"}, {Value: "quarterly", Label: "Quarterly"}, {Value: "yearly", Label: "Yearly"}, }, orm.FieldOpts{String: "Period", Required: true, Default: "monthly"}), orm.Date("date_next", orm.FieldOpts{String: "Next Date", Required: true}), orm.Date("date_end", orm.FieldOpts{String: "End Date"}), orm.Many2one("template_move_id", "account.move", orm.FieldOpts{String: "Template Entry"}), orm.Boolean("active", orm.FieldOpts{String: "Active", Default: true}), orm.Selection("state", []orm.SelectionItem{ {Value: "draft", Label: "Draft"}, {Value: "running", Label: "Running"}, {Value: "done", Label: "Done"}, }, orm.FieldOpts{String: "Status", Default: "draft"}), ) // action_start: draft -> running m.RegisterMethod("action_start", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) { env := rs.Env() for _, id := range rs.IDs() { env.Tx().Exec(env.Ctx(), `UPDATE account_move_recurring SET state = 'running' WHERE id = $1 AND state = 'draft'`, id) } return true, nil }) // action_done: running -> done m.RegisterMethod("action_done", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) { env := rs.Env() for _, id := range rs.IDs() { env.Tx().Exec(env.Ctx(), `UPDATE account_move_recurring SET state = 'done' WHERE id = $1`, id) } return true, nil }) // action_generate: create journal entries from the template and advance next date. // Mirrors: odoo/addons/account/models/account_move.py _cron_recurring_entries() m.RegisterMethod("action_generate", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) { env := rs.Env() for _, recID := range rs.IDs() { var templateID *int64 var dateNext, period string var dateEnd *string err := env.Tx().QueryRow(env.Ctx(), `SELECT template_move_id, date_next::text, period, date_end::text FROM account_move_recurring WHERE id = $1 AND state = 'running'`, recID, ).Scan(&templateID, &dateNext, &period, &dateEnd) if err != nil { continue // not running or not found } if templateID == nil || *templateID == 0 { continue } // Check if past end date if dateEnd != nil && dateNext > *dateEnd { env.Tx().Exec(env.Ctx(), `UPDATE account_move_recurring SET state = 'done' WHERE id = $1`, recID) continue } // Copy the template move with the next date templateRS := env.Model("account.move").Browse(*templateID) newMove, err := templateRS.Copy(orm.Values{"date": dateNext}) if err != nil { continue } _ = newMove // Advance next date based on period var interval string switch period { case "daily": interval = "1 day" case "weekly": interval = "7 days" case "monthly": interval = "1 month" case "quarterly": interval = "3 months" case "yearly": interval = "1 year" default: interval = "1 month" } env.Tx().Exec(env.Ctx(), `UPDATE account_move_recurring SET date_next = date_next + $1::interval WHERE id = $2`, interval, recID) } return true, nil }) }