odoo.py from . import mymodule $ odoo.py scaffold <module name> <where to put it> # -*- coding: utf-8 -*- { 'name': "Open Academy", 'summary': """Manage trainings""", 'description': """ Open Academy module for managing trainings: - training courses - training sessions - attendees registration """, 'author': "My Company", 'website': "http://www.yourcompany.com", # Categories can be used to filter modules in modules listing # Check https://github.com/odoo/odoo/blob/master/openerp/addons/base/module/module_data.xml # for the full list 'category': 'Test', 'version': '0.1', # any module necessary for this one to work correctly 'depends': ['base'], # always loaded 'data': [ # 'security/ir.model.access.csv', 'templates.xml', ], # only loaded in demonstration mode 'demo': [ 'demo.xml', ], } # -*- coding: utf-8 -*- from . import controllers from . import models # -*- coding: utf-8 -*- from openerp import http # class Openacademy(http.Controller): # @http.route('/openacademy/openacademy/', auth='public') # def index(self, **kw): # return "Hello, world" # @http.route('/openacademy/openacademy/objects/', auth='public') # def list(self, **kw): # return http.request.render('openacademy.listing', { # 'root': '/openacademy/openacademy', # 'objects': http.request.env['openacademy.openacademy'].search([]), # }) # @http.route('/openacademy/openacademy/objects/<model("openacademy.openacademy"):obj>/', auth='public') # def object(self, obj, **kw): # return http.request.render('openacademy.object', { # 'object': obj # }) <openerp> <data> <!-- --> <!-- <record id="object0" model="openacademy.openacademy"> --> <!-- <field name="name">Object 0</field> --> <!-- </record> --> <!-- --> <!-- <record id="object1" model="openacademy.openacademy"> --> <!-- <field name="name">Object 1</field> --> <!-- </record> --> <!-- --> <!-- <record id="object2" model="openacademy.openacademy"> --> <!-- <field name="name">Object 2</field> --> <!-- </record> --> <!-- --> <!-- <record id="object3" model="openacademy.openacademy"> --> <!-- <field name="name">Object 3</field> --> <!-- </record> --> <!-- --> <!-- <record id="object4" model="openacademy.openacademy"> --> <!-- <field name="name">Object 4</field> --> <!-- </record> --> <!-- --> </data> </openerp> # -*- coding: utf-8 -*- from openerp import models, fields, api # class openacademy(models.Model): # _name = 'openacademy.openacademy' # name = fields.Char() id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink access_openacademy_openacademy,openacademy.openacademy,model_openacademy_openacademy,,1,0,0,0 <openerp> <data> <!-- <template id="listing"> --> <!-- <ul> --> <!-- <li t-foreach="objects" t-as="object"> --> <!-- <a t-attf-href="{{ root }}/objects/{{ object.id }}"> --> <!-- <t t-esc="object.display_name"/> --> <!-- </a> --> <!-- </li> --> <!-- </ul> --> <!-- </template> --> <!-- <template id="object"> --> <!-- <h1><t t-esc="object.display_name"/></h1> --> <!-- <dl> --> <!-- <t t-foreach="object._fields" t-as="field"> --> <!-- <dt><t t-esc="field"/></dt> --> <!-- <dd><t t-esc="object[field]"/></dd> --> <!-- </t> --> <!-- </dl> --> <!-- </template> --> </data> </openerp> from openerp import models class MinimalModel(models.Model): _name = 'test.model' from openerp import models, fields class LessMinimalModel(models.Model): _name = 'test.model2' name = fields.Char() name = field.Char(required=True) from openerp import models, fields, api class Course(models.Model): _name = 'openacademy.course' name = fields.Char(string="Title", required=True) description = fields.Text() <openerp> <data> <record model="{model name}" id="{record identifier}"> <field name="{a field name}">{a value}</field> </record> </data> </openerp> <openerp> <data> <record model="openacademy.course" id="course0"> <field name="name">Course 0</field> <field name="description">Course 0's description Can have multiple lines </field> </record> <record model="openacademy.course" id="course1"> <field name="name">Course 1</field> <!-- no description for this one --> </record> <record model="openacademy.course" id="course2"> <field name="name">Course 2</field> <field name="description">Course 2's description</field> </record> </data> </openerp> <record model="ir.actions.act_window" id="action_list_ideas"> <field name="name">Ideas</field> <field name="res_model">idea.idea</field> <field name="view_mode">tree,form</field> </record> <menuitem id="menu_ideas" parent="menu_root" name="Ideas" sequence="10" action="action_list_ideas"/> 'data': [ # 'security/ir.model.access.csv', 'templates.xml', 'views/openacademy.xml', ], # only loaded in demonstration mode 'demo': [ <?xml version="1.0" encoding="UTF-8"?> <openerp> <data> <!-- window action --> <!-- The following tag is an action definition for a "window action", that is an action opening a view or a set of views --> <record model="ir.actions.act_window" id="course_list_action"> <field name="name">Courses</field> <field name="res_model">openacademy.course</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> <field name="help" type="html"> <p class="oe_view_nocontent_create">Create the first course </p> </field> </record> <!-- top level menu: no parent --> <menuitem id="main_openacademy_menu" name="Open Academy"/> <!-- A first level in the left side menu is needed before using action= attribute --> <menuitem id="openacademy_menu" name="Open Academy" parent="main_openacademy_menu"/> <!-- the following menuitem should appear *after* its parent openacademy_menu and *after* its action course_list_action --> <menuitem id="courses_menu" name="Courses" parent="openacademy_menu" action="course_list_action"/> <!-- Full id location: action="openacademy.course_list_action" It is not required when it is the same module --> </data> </openerp> <record model="ir.ui.view" id="view_id"> <field name="name">view.name</field> <field name="model">object_name</field> <field name="priority" eval="16"/> <field name="arch" type="xml"> <!-- view content: <form>, <tree>, <graph>, ... --> </field> </record> <tree string="Idea list"> <field name="name"/> <field name="inventor_id"/> </tree> <form string="Idea form"> <group colspan="4"> <group colspan="2" col="2"> <separator string="General stuff" colspan="2"/> <field name="name"/> <field name="inventor_id"/> </group> <group colspan="2" col="2"> <separator string="Dates" colspan="2"/> <field name="active"/> <field name="invent_date" readonly="1"/> </group> <notebook colspan="4"> <page string="Description"> <field name="description" nolabel="1"/> </page> </notebook> <field name="state"/> </group> </form> <?xml version="1.0" encoding="UTF-8"?> <openerp> <data> <record model="ir.ui.view" id="course_form_view"> <field name="name">course.form</field> <field name="model">openacademy.course</field> <field name="arch" type="xml"> <form string="Course Form"> <sheet> <group> <field name="name"/> <field name="description"/> </group> </sheet> </form> </field> </record> <!-- window action --> <!-- The following tag is an action definition for a "window action", <sheet> <group> <field name="name"/> </group> <notebook> <page string="Description"> <field name="description"/> </page> <page string="About"> This is an example of notebooks </page> </notebook> </sheet> </form> </field> <form string="Idea Form"> <header> <button string="Confirm" type="object" name="action_confirm" states="draft" class="oe_highlight" /> <button string="Mark as done" type="object" name="action_done" states="confirmed" class="oe_highlight"/> <button string="Reset to draft" type="object" name="action_draft" states="confirmed,done" /> <field name="state" widget="statusbar"/> </header> <sheet> <div class="oe_title"> <label for="name" class="oe_edit_only" string="Idea Name" /> <h1><field name="name" /></h1> </div> <separator string="General" colspan="2" /> <group colspan="2" col="2"> <field name="description" placeholder="Idea description..." /> </group> </sheet> </form> <search> <field name="name"/> <field name="inventor_id"/> </search> </field> </record> <record model="ir.ui.view" id="course_search_view"> <field name="name">course.search</field> <field name="model">openacademy.course</field> <field name="arch" type="xml"> <search> <field name="name"/> <field name="description"/> </search> </field> </record> <!-- window action --> <!-- The following tag is an action definition for a "window action", name = fields.Char(string="Title", required=True) description = fields.Text() class Session(models.Model): _name = 'openacademy.session' name = fields.Char(required=True) start_date = fields.Date() duration = fields.Float(digits=(6, 2), help="Duration in days") seats = fields.Integer(string="Number of seats") <!-- Full id location: action="openacademy.course_list_action" It is not required when it is the same module --> <!-- session form view --> <record model="ir.ui.view" id="session_form_view"> <field name="name">session.form</field> <field name="model">openacademy.session</field> <field name="arch" type="xml"> <form string="Session Form"> <sheet> <group> <field name="name"/> <field name="start_date"/> <field name="duration"/> <field name="seats"/> </group> </sheet> </form> </field> </record> <record model="ir.actions.act_window" id="session_list_action"> <field name="name">Sessions</field> <field name="res_model">openacademy.session</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> </record> <menuitem id="session_menu" name="Sessions" parent="openacademy_menu" action="session_list_action"/> </data> </openerp> print foo.other_id.name for other in foo.other_ids: print other.name for other in foo.other_ids: print other.name name = fields.Char(string="Title", required=True) description = fields.Text() responsible_id = fields.Many2one('res.users', ondelete='set null', string="Responsible", index=True) class Session(models.Model): _name = 'openacademy.session' start_date = fields.Date() duration = fields.Float(digits=(6, 2), help="Duration in days") seats = fields.Integer(string="Number of seats") instructor_id = fields.Many2one('res.partner', string="Instructor") course_id = fields.Many2one('openacademy.course', ondelete='cascade', string="Course", required=True) <sheet> <group> <field name="name"/> <field name="responsible_id"/> </group> <notebook> <page string="Description"> </field> </record> <!-- override the automatically generated list view for courses --> <record model="ir.ui.view" id="course_tree_view"> <field name="name">course.tree</field> <field name="model">openacademy.course</field> <field name="arch" type="xml"> <tree string="Course Tree"> <field name="name"/> <field name="responsible_id"/> </tree> </field> </record> <!-- window action --> <!-- The following tag is an action definition for a "window action", <form string="Session Form"> <sheet> <group> <group string="General"> <field name="course_id"/> <field name="name"/> <field name="instructor_id"/> </group> <group string="Schedule"> <field name="start_date"/> <field name="duration"/> <field name="seats"/> </group> </group> </sheet> </form> </field> </record> <!-- session tree/list view --> <record model="ir.ui.view" id="session_tree_view"> <field name="name">session.tree</field> <field name="model">openacademy.session</field> <field name="arch" type="xml"> <tree string="Session Tree"> <field name="name"/> <field name="course_id"/> </tree> </field> </record> <record model="ir.actions.act_window" id="session_list_action"> <field name="name">Sessions</field> <field name="res_model">openacademy.session</field> responsible_id = fields.Many2one('res.users', ondelete='set null', string="Responsible", index=True) session_ids = fields.One2many( 'openacademy.session', 'course_id', string="Sessions") class Session(models.Model): <page string="Description"> <field name="description"/> </page> <page string="Sessions"> <field name="session_ids"> <tree string="Registered sessions"> <field name="name"/> <field name="instructor_id"/> </tree> </field> </page> </notebook> </sheet> instructor_id = fields.Many2one('res.partner', string="Instructor") course_id = fields.Many2one('openacademy.course', ondelete='cascade', string="Course", required=True) attendee_ids = fields.Many2many('res.partner', string="Attendees") <field name="seats"/> </group> </group> <label for="attendee_ids"/> <field name="attendee_ids"/> </sheet> </form> </field> <!-- improved idea categories list --> <record id="idea_category_list2" model="ir.ui.view"> <field name="name">id.category.list2</field> <field name="model">idea.category</field> <field name="inherit_id" ref="id_category_list"/> <field name="arch" type="xml"> <!-- find field description and add the field idea_ids after it --> <xpath expr="//field[@name='description']" position="after"> <field name="idea_ids" string="Number of ideas"/> </xpath> </field> </record> <xpath expr="//field[@name='description']" position="after"> <field name="idea_ids" /> </xpath> <field name="description" position="after"> <field name="idea_ids" /> </field> # -*- coding: utf-8 -*- from . import controllers from . import models from . import partner # 'security/ir.model.access.csv', 'templates.xml', 'views/openacademy.xml', 'views/partner.xml', ], # only loaded in demonstration mode 'demo': [ # -*- coding: utf-8 -*- from openerp import fields, models class Partner(models.Model): _inherit = 'res.partner' # Add a new column to the res.partner model, by default partners are not # instructors instructor = fields.Boolean("Instructor", default=False) session_ids = fields.Many2many('openacademy.session', string="Attended Sessions", readonly=True) <?xml version="1.0" encoding="UTF-8"?> <openerp> <data> <!-- Add instructor field to existing view --> <record model="ir.ui.view" id="partner_instructor_form_view"> <field name="name">partner.instructor</field> <field name="model">res.partner</field> <field name="inherit_id" ref="base.view_partner_form"/> <field name="arch" type="xml"> <notebook position="inside"> <page string="Sessions"> <group> <field name="instructor"/> <field name="session_ids"/> </group> </page> </notebook> </field> </record> <record model="ir.actions.act_window" id="contact_list_action"> <field name="name">Contacts</field> <field name="res_model">res.partner</field> <field name="view_mode">tree,form</field> </record> <menuitem id="configuration_menu" name="Configuration" parent="main_openacademy_menu"/> <menuitem id="contact_menu" name="Contacts" parent="configuration_menu" action="contact_list_action"/> </data> </openerp> [('product_type', '=', 'service'), ('unit_price', '>', 1000)] ['|', ('product_type', '=', 'service'), '!', '&', ('unit_price', '>=', 1000), ('unit_price', '<', 2000)] duration = fields.Float(digits=(6, 2), help="Duration in days") seats = fields.Integer(string="Number of seats") instructor_id = fields.Many2one('res.partner', string="Instructor", domain=[('instructor', '=', True)]) course_id = fields.Many2one('openacademy.course', ondelete='cascade', string="Course", required=True) attendee_ids = fields.Many2many('res.partner', string="Attendees") seats = fields.Integer(string="Number of seats") instructor_id = fields.Many2one('res.partner', string="Instructor", domain=['|', ('instructor', '=', True), ('category_id.name', 'ilike', "Teacher")]) course_id = fields.Many2one('openacademy.course', ondelete='cascade', string="Course", required=True) attendee_ids = fields.Many2many('res.partner', string="Attendees") <menuitem id="contact_menu" name="Contacts" parent="configuration_menu" action="contact_list_action"/> <record model="ir.actions.act_window" id="contact_cat_list_action"> <field name="name">Contact Tags</field> <field name="res_model">res.partner.category</field> <field name="view_mode">tree,form</field> </record> <menuitem id="contact_cat_menu" name="Contact Tags" parent="configuration_menu" action="contact_cat_list_action"/> <record model="res.partner.category" id="teacher1"> <field name="name">Teacher / Level 1</field> </record> <record model="res.partner.category" id="teacher2"> <field name="name">Teacher / Level 2</field> </record> </data> </openerp> import random from openerp import models, fields, api class ComputedModel(models.Model): _name = 'test.computed' name = fields.Char(compute='_compute_name') @api.multi def _compute_name(self): for record in self: record.name = str(random.randint(1, 1e6)) from openerp import models, fields, api class ComputedModel(models.Model): _name = 'test.computed' name = fields.Char(compute='_compute_name') value = fields.Integer() @api.depends('value') def _compute_name(self): for record in self: record.name = "Record with value %s" % record.value course_id = fields.Many2one('openacademy.course', ondelete='cascade', string="Course", required=True) attendee_ids = fields.Many2many('res.partner', string="Attendees") taken_seats = fields.Float(string="Taken seats", compute='_taken_seats') @api.depends('seats', 'attendee_ids') def _taken_seats(self): for r in self: if not r.seats: r.taken_seats = 0.0 else: r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats <field name="start_date"/> <field name="duration"/> <field name="seats"/> <field name="taken_seats" widget="progressbar"/> </group> </group> <label for="attendee_ids"/> <tree string="Session Tree"> <field name="name"/> <field name="course_id"/> <field name="taken_seats" widget="progressbar"/> </tree> </field> </record> name = fields.Char(default="Unknown") user_id = fields.Many2one('res.users', default=lambda self: self.env.user) _name = 'openacademy.session' name = fields.Char(required=True) start_date = fields.Date(default=fields.Date.today) duration = fields.Float(digits=(6, 2), help="Duration in days") seats = fields.Integer(string="Number of seats") active = fields.Boolean(default=True) instructor_id = fields.Many2one('res.partner', string="Instructor", domain=['|', ('instructor', '=', True), <field name="course_id"/> <field name="name"/> <field name="instructor_id"/> <field name="active"/> </group> <group string="Schedule"> <field name="start_date"/> <!-- content of form view --> <field name="amount"/> <field name="unit_price"/> <field name="price" readonly="1"/> # onchange handler @api.onchange('amount', 'unit_price') def _onchange_price(self): # set auto-changing field self.price = self.amount * self.unit_price # Can optionally return a warning and domains return { 'warning': { 'title': "Something bad happened", 'message': "It was very bad indeed", } } r.taken_seats = 0.0 else: r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats @api.onchange('seats', 'attendee_ids') def _verify_valid_seats(self): if self.seats < 0: return { 'warning': { 'title': "Incorrect 'seats' value", 'message': "The number of available seats may not be negative", }, } if self.seats < len(self.attendee_ids): return { 'warning': { 'title': "Too many attendees", 'message': "Increase seats or remove excess attendees", }, } from openerp.exceptions import ValidationError @api.constrains('age') def _check_something(self): for record in self: if record.age > 20: raise ValidationError("Your record is too old: %s" % record.age) # all records passed the test, don't return anything # -*- coding: utf-8 -*- from openerp import models, fields, api, exceptions class Course(models.Model): _name = 'openacademy.course' 'message': "Increase seats or remove excess attendees", }, } @api.constrains('instructor_id', 'attendee_ids') def _check_instructor_not_in_attendees(self): for r in self: if r.instructor_id and r.instructor_id in r.attendee_ids: raise exceptions.ValidationError("A session's instructor can't be an attendee") session_ids = fields.One2many( 'openacademy.session', 'course_id', string="Sessions") _sql_constraints = [ ('name_description_check', 'CHECK(name != description)', "The title of the course should not be the description"), ('name_unique', 'UNIQUE(name)', "The course title must be unique"), ] class Session(models.Model): _name = 'openacademy.session' session_ids = fields.One2many( 'openacademy.session', 'course_id', string="Sessions") @api.multi def copy(self, default=None): default = dict(default or {}) copied_count = self.search_count( [('name', '=like', u"Copy of {}%".format(self.name))]) if not copied_count: new_name = u"Copy of {}".format(self.name) else: new_name = u"Copy of {} ({})".format(self.name, copied_count) default['name'] = new_name return super(Course, self).copy(default) _sql_constraints = [ ('name_description_check', 'CHECK(name != description)', <tree string="Idea Categories" decoration-info="state=='draft'" decoration-danger="state=='trashed'"> <field name="name"/> <field name="state"/> </tree> <field name="name">session.tree</field> <field name="model">openacademy.session</field> <field name="arch" type="xml"> <tree string="Session Tree" decoration-info="duration<5" decoration-danger="duration>15"> <field name="name"/> <field name="course_id"/> <field name="duration" invisible="1"/> <field name="taken_seats" widget="progressbar"/> </tree> </field> <calendar string="Ideas" date_start="invent_date" color="inventor_id"> <field name="name"/> </calendar> # -*- coding: utf-8 -*- from datetime import timedelta from openerp import models, fields, api, exceptions class Course(models.Model): attendee_ids = fields.Many2many('res.partner', string="Attendees") taken_seats = fields.Float(string="Taken seats", compute='_taken_seats') end_date = fields.Date(string="End Date", store=True, compute='_get_end_date', inverse='_set_end_date') @api.depends('seats', 'attendee_ids') def _taken_seats(self): }, } @api.depends('start_date', 'duration') def _get_end_date(self): for r in self: if not (r.start_date and r.duration): r.end_date = r.start_date continue # Add duration to start_date, but: Monday + 5 days = Saturday, so # subtract one second to get on Friday instead start = fields.Datetime.from_string(r.start_date) duration = timedelta(days=r.duration, seconds=-1) r.end_date = start + duration def _set_end_date(self): for r in self: if not (r.start_date and r.end_date): continue # Compute the difference between dates, but: Friday - Monday = 4 days, # so add one day to get 5 days instead start_date = fields.Datetime.from_string(r.start_date) end_date = fields.Datetime.from_string(r.end_date) r.duration = (end_date - start_date).days + 1 @api.constrains('instructor_id', 'attendee_ids') def _check_instructor_not_in_attendees(self): for r in self: </field> </record> <!-- calendar view --> <record model="ir.ui.view" id="session_calendar_view"> <field name="name">session.calendar</field> <field name="model">openacademy.session</field> <field name="arch" type="xml"> <calendar string="Session Calendar" date_start="start_date" date_stop="end_date" color="instructor_id"> <field name="name"/> </calendar> </field> </record> <record model="ir.actions.act_window" id="session_list_action"> <field name="name">Sessions</field> <field name="res_model">openacademy.session</field> <field name="view_type">form</field> <field name="view_mode">tree,form,calendar</field> </record> <menuitem id="session_menu" name="Sessions" <search string="Ideas"> <field name="name"/> <field name="description" string="Name and description" filter_domain="['|', ('name', 'ilike', self), ('description', 'ilike', self)]"/> <field name="inventor_id"/> <field name="country_id" widget="selection"/> <filter name="my_ideas" string="My Ideas" domain="[('inventor_id', '=', uid)]"/> <group string="Group By"> <filter name="group_by_inventor" string="Inventor" context="{'group_by': 'inventor_id'}"/> </group> </search> <search> <field name="name"/> <field name="description"/> <filter name="my_courses" string="My Courses" domain="[('responsible_id', '=', uid)]"/> <group string="Group By"> <filter name="by_responsible" string="Responsible" context="{'group_by': 'responsible_id'}"/> </group> </search> </field> </record> <field name="res_model">openacademy.course</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> <field name="context" eval="{'search_default_my_courses': 1}"/> <field name="help" type="html"> <p class="oe_view_nocontent_create">Create the first course </p> <gantt string="Ideas" date_start="invent_date" date_stop="date_finished" progress="progress" default_group_by="inventor_id" /> end_date = fields.Date(string="End Date", store=True, compute='_get_end_date', inverse='_set_end_date') hours = fields.Float(string="Duration in hours", compute='_get_hours', inverse='_set_hours') @api.depends('seats', 'attendee_ids') def _taken_seats(self): for r in self: end_date = fields.Datetime.from_string(r.end_date) r.duration = (end_date - start_date).days + 1 @api.depends('duration') def _get_hours(self): for r in self: r.hours = r.duration * 24 def _set_hours(self): for r in self: r.duration = r.hours / 24 @api.constrains('instructor_id', 'attendee_ids') def _check_instructor_not_in_attendees(self): for r in self: </field> </record> <record model="ir.ui.view" id="session_gantt_view"> <field name="name">session.gantt</field> <field name="model">openacademy.session</field> <field name="arch" type="xml"> <gantt string="Session Gantt" color="course_id" date_start="start_date" date_delay="hours" default_group_by='instructor_id'> <field name="name"/> </gantt> </field> </record> <record model="ir.actions.act_window" id="session_list_action"> <field name="name">Sessions</field> <field name="res_model">openacademy.session</field> <field name="view_type">form</field> <field name="view_mode">tree,form,calendar,gantt</field> </record> <menuitem id="session_menu" name="Sessions" <graph string="Total idea score by Inventor"> <field name="inventor_id"/> <field name="score" type="measure"/> </graph> hours = fields.Float(string="Duration in hours", compute='_get_hours', inverse='_set_hours') attendees_count = fields.Integer( string="Attendees count", compute='_get_attendees_count', store=True) @api.depends('seats', 'attendee_ids') def _taken_seats(self): for r in self: for r in self: r.duration = r.hours / 24 @api.depends('attendee_ids') def _get_attendees_count(self): for r in self: r.attendees_count = len(r.attendee_ids) @api.constrains('instructor_id', 'attendee_ids') def _check_instructor_not_in_attendees(self): for r in self: </field> </record> <record model="ir.ui.view" id="openacademy_session_graph_view"> <field name="name">openacademy.session.graph</field> <field name="model">openacademy.session</field> <field name="arch" type="xml"> <graph string="Participations by Courses"> <field name="course_id"/> <field name="attendees_count" type="measure"/> </graph> </field> </record> <record model="ir.actions.act_window" id="session_list_action"> <field name="name">Sessions</field> <field name="res_model">openacademy.session</field> <field name="view_type">form</field> <field name="view_mode">tree,form,calendar,gantt,graph</field> </record> <menuitem id="session_menu" name="Sessions" duration = fields.Float(digits=(6, 2), help="Duration in days") seats = fields.Integer(string="Number of seats") active = fields.Boolean(default=True) color = fields.Integer() instructor_id = fields.Many2one('res.partner', string="Instructor", domain=['|', ('instructor', '=', True), </record> <record model="ir.ui.view" id="view_openacad_session_kanban"> <field name="name">openacad.session.kanban</field> <field name="model">openacademy.session</field> <field name="arch" type="xml"> <kanban default_group_by="course_id"> <field name="color"/> <templates> <t t-name="kanban-box"> <div t-attf-class="oe_kanban_color_{{kanban_getcolor(record.color.raw_value)}} oe_kanban_global_click_edit oe_semantic_html_override oe_kanban_card {{record.group_fancy==1 ? 'oe_kanban_card_fancy' : ''}}"> <div class="oe_dropdown_kanban"> <!-- dropdown menu --> <div class="oe_dropdown_toggle"> <i class="fa fa-bars fa-lg"/> <ul class="oe_dropdown_menu"> <li> <a type="delete">Delete</a> </li> <li> <ul class="oe_kanban_colorpicker" data-field="color"/> </li> </ul> </div> <div class="oe_clear"></div> </div> <div t-attf-class="oe_kanban_content"> <!-- title --> Session name: <field name="name"/> <br/> Start date: <field name="start_date"/> <br/> duration: <field name="duration"/> </div> </div> </t> </templates> </kanban> </field> </record> <record model="ir.actions.act_window" id="session_list_action"> <field name="name">Sessions</field> <field name="res_model">openacademy.session</field> <field name="view_type">form</field> <field name="view_mode">tree,form,calendar,gantt,graph,kanban</field> </record> <menuitem id="session_menu" name="Sessions" parent="openacademy_menu" attendees_count = fields.Integer( string="Attendees count", compute='_get_attendees_count', store=True) state = fields.Selection([ ('draft', "Draft"), ('confirmed', "Confirmed"), ('done', "Done"), ], default='draft') @api.multi def action_draft(self): self.state = 'draft' @api.multi def action_confirm(self): self.state = 'confirmed' @api.multi def action_done(self): self.state = 'done' @api.depends('seats', 'attendee_ids') def _taken_seats(self): for r in self: <field name="model">openacademy.session</field> <field name="arch" type="xml"> <form string="Session Form"> <header> <button name="action_draft" type="object" string="Reset to draft" states="confirmed,done"/> <button name="action_confirm" type="object" string="Confirm" states="draft" class="oe_highlight"/> <button name="action_done" type="object" string="Mark as done" states="confirmed" class="oe_highlight"/> <field name="state" widget="statusbar"/> </header> <sheet> <group> <group string="General"> 'templates.xml', 'views/openacademy.xml', 'views/partner.xml', 'views/session_workflow.xml', ], # only loaded in demonstration mode 'demo': [ ('draft', "Draft"), ('confirmed', "Confirmed"), ('done', "Done"), ]) @api.multi def action_draft(self): <field name="arch" type="xml"> <form string="Session Form"> <header> <button name="draft" type="workflow" string="Reset to draft" states="confirmed,done"/> <button name="confirm" type="workflow" string="Confirm" states="draft" class="oe_highlight"/> <button name="done" type="workflow" string="Mark as done" states="confirmed" class="oe_highlight"/> <field name="state" widget="statusbar"/> <openerp> <data> <record model="workflow" id="wkf_session"> <field name="name">OpenAcademy sessions workflow</field> <field name="osv">openacademy.session</field> <field name="on_create">True</field> </record> <record model="workflow.activity" id="draft"> <field name="name">Draft</field> <field name="wkf_id" ref="wkf_session"/> <field name="flow_start" eval="True"/> <field name="kind">function</field> <field name="action">action_draft()</field> </record> <record model="workflow.activity" id="confirmed"> <field name="name">Confirmed</field> <field name="wkf_id" ref="wkf_session"/> <field name="kind">function</field> <field name="action">action_confirm()</field> </record> <record model="workflow.activity" id="done"> <field name="name">Done</field> <field name="wkf_id" ref="wkf_session"/> <field name="kind">function</field> <field name="action">action_done()</field> </record> <record model="workflow.transition" id="session_draft_to_confirmed"> <field name="act_from" ref="draft"/> <field name="act_to" ref="confirmed"/> <field name="signal">confirm</field> </record> <record model="workflow.transition" id="session_confirmed_to_draft"> <field name="act_from" ref="confirmed"/> <field name="act_to" ref="draft"/> <field name="signal">draft</field> </record> <record model="workflow.transition" id="session_done_to_draft"> <field name="act_from" ref="done"/> <field name="act_to" ref="draft"/> <field name="signal">draft</field> </record> <record model="workflow.transition" id="session_confirmed_to_done"> <field name="act_from" ref="confirmed"/> <field name="act_to" ref="done"/> <field name="signal">done</field> </record> </data> </openerp> <field name="act_to" ref="done"/> <field name="signal">done</field> </record> <record model="workflow.transition" id="session_auto_confirm_half_filled"> <field name="act_from" ref="draft"/> <field name="act_to" ref="confirmed"/> <field name="condition">taken_seats > 50</field> </record> </data> </openerp> <field name="on_create">True</field> </record> <record model="ir.actions.server" id="set_session_to_draft"> <field name="name">Set session to Draft</field> <field name="model_id" ref="model_openacademy_session"/> <field name="code"> model.search([('id', 'in', context['active_ids'])]).action_draft() </field> </record> <record model="workflow.activity" id="draft"> <field name="name">Draft</field> <field name="wkf_id" ref="wkf_session"/> <field name="flow_start" eval="True"/> <field name="kind">dummy</field> <field name="action"></field> <field name="action_id" ref="set_session_to_draft"/> </record> <record model="ir.actions.server" id="set_session_to_confirmed"> <field name="name">Set session to Confirmed</field> <field name="model_id" ref="model_openacademy_session"/> <field name="code"> model.search([('id', 'in', context['active_ids'])]).action_confirm() </field> </record> <record model="workflow.activity" id="confirmed"> <field name="name">Confirmed</field> <field name="wkf_id" ref="wkf_session"/> <field name="kind">dummy</field> <field name="action"></field> <field name="action_id" ref="set_session_to_confirmed"/> </record> <record model="ir.actions.server" id="set_session_to_done"> <field name="name">Set session to Done</field> <field name="model_id" ref="model_openacademy_session"/> <field name="code"> model.search([('id', 'in', context['active_ids'])]).action_done() </field> </record> <record model="workflow.activity" id="done"> <field name="name">Done</field> <field name="wkf_id" ref="wkf_session"/> <field name="kind">dummy</field> <field name="action"></field> <field name="action_id" ref="set_session_to_done"/> </record> <record model="workflow.transition" id="session_draft_to_confirmed"> id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink access_idea_idea,idea.idea,model_idea_idea,base.group_user,1,1,1,0 access_idea_vote,idea.vote,model_idea_vote,base.group_user,1,1,1,0 # always loaded 'data': [ 'security/security.xml', 'security/ir.model.access.csv', 'templates.xml', 'views/openacademy.xml', 'views/partner.xml', id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink course_manager,course manager,model_openacademy_course,group_manager,1,1,1,1 session_manager,session manager,model_openacademy_session,group_manager,1,1,1,1 course_read_all,course all,model_openacademy_course,,1,0,0,0 session_read_all,session all,model_openacademy_session,,1,0,0,0 <openerp> <data> <record id="group_manager" model="res.groups"> <field name="name">OpenAcademy / Manager</field> </record> </data> </openerp> <record id="delete_cancelled_only" model="ir.rule"> <field name="name">Only cancelled leads may be deleted</field> <field name="model_id" ref="crm.model_crm_lead"/> <field name="groups" eval="[(4, ref('base.group_sale_manager'))]"/> <field name="perm_read" eval="0"/> <field name="perm_write" eval="0"/> <field name="perm_create" eval="0"/> <field name="perm_unlink" eval="1" /> <field name="domain_force">[('state','=','cancel')]</field> </record> <record id="group_manager" model="res.groups"> <field name="name">OpenAcademy / Manager</field> </record> <record id="only_responsible_can_modify" model="ir.rule"> <field name="name">Only Responsible can modify Course</field> <field name="model_id" ref="model_openacademy_course"/> <field name="groups" eval="[(4, ref('openacademy.group_manager'))]"/> <field name="perm_read" eval="0"/> <field name="perm_write" eval="1"/> <field name="perm_create" eval="0"/> <field name="perm_unlink" eval="1"/> <field name="domain_force"> ['|', ('responsible_id','=',False), ('responsible_id','=',user.id)] </field> </record> </data> </openerp> from . import controllers from . import models from . import partner from . import wizard # -*- coding: utf-8 -*- from openerp import models, fields, api class Wizard(models.TransientModel): _name = 'openacademy.wizard' session_id = fields.Many2one('openacademy.session', string="Session", required=True) attendee_ids = fields.Many2many('res.partner', string="Attendees") <act_window id="launch_the_wizard" name="Launch the Wizard" src_model="context.model.name" res_model="wizard.model.name" view_mode="form" target="new" key2="client_action_multi"/> class Wizard(models.TransientModel): _name = 'openacademy.wizard' def _default_session(self): return self.env['openacademy.session'].browse(self._context.get('active_id')) session_id = fields.Many2one('openacademy.session', string="Session", required=True, default=_default_session) attendee_ids = fields.Many2many('res.partner', string="Attendees") parent="openacademy_menu" action="session_list_action"/> <record model="ir.ui.view" id="wizard_form_view"> <field name="name">wizard.form</field> <field name="model">openacademy.wizard</field> <field name="arch" type="xml"> <form string="Add Attendees"> <group> <field name="session_id"/> <field name="attendee_ids"/> </group> </form> </field> </record> <act_window id="launch_session_wizard" name="Add Attendees" src_model="openacademy.session" res_model="openacademy.wizard" view_mode="form" target="new" key2="client_action_multi"/> </data> </openerp> <field name="attendee_ids"/> </group> <footer> <button name="subscribe" type="object" string="Subscribe" class="oe_highlight"/> or <button special="cancel" string="Cancel"/> </footer> </form> </field> </record> session_id = fields.Many2one('openacademy.session', string="Session", required=True, default=_default_session) attendee_ids = fields.Many2many('res.partner', string="Attendees") @api.multi def subscribe(self): self.session_id.attendee_ids |= self.attendee_ids return {} <form string="Add Attendees"> <group> <field name="session_ids"/> <field name="attendee_ids"/> </group> <footer> <button name="subscribe" type="object" class Wizard(models.TransientModel): _name = 'openacademy.wizard' def _default_sessions(self): return self.env['openacademy.session'].browse(self._context.get('active_ids')) session_ids = fields.Many2many('openacademy.session', string="Sessions", required=True, default=_default_sessions) attendee_ids = fields.Many2many('res.partner', string="Attendees") @api.multi def subscribe(self): for session in self.session_ids: session.attendee_ids |= self.attendee_ids return {} |- idea/ # The module directory |- i18n/ # Translation files | - idea.pot # Translation Template (exported from Odoo) | - fr.po # French translation | - pt_BR.po # Brazilian Portuguese translation | (...) # -*- coding: utf-8 -*- from datetime import timedelta from openerp import models, fields, api, exceptions, _ class Course(models.Model): _name = 'openacademy.course' default = dict(default or {}) copied_count = self.search_count( [('name', '=like', _(u"Copy of {}%").format(self.name))]) if not copied_count: new_name = _(u"Copy of {}").format(self.name) else: new_name = _(u"Copy of {} ({})").format(self.name, copied_count) default['name'] = new_name return super(Course, self).copy(default) if self.seats < 0: return { 'warning': { 'title': _("Incorrect 'seats' value"), 'message': _("The number of available seats may not be negative"), }, } if self.seats < len(self.attendee_ids): return { 'warning': { 'title': _("Too many attendees"), 'message': _("Increase seats or remove excess attendees"), }, } def _check_instructor_not_in_attendees(self): for r in self: if r.instructor_id and r.instructor_id in r.attendee_ids: raise exceptions.ValidationError(_("A session's instructor can't be an attendee")) <report id="account_invoices" model="account.invoice" string="Invoices" report_type="qweb-pdf" name="account.report_invoice" file="account.report_invoice" attachment_use="True" attachment="(object.state in ('open','paid')) and ('INV'+(object.number or '').replace('/','')+'.pdf')" /> <t t-call="report.html_container"> <t t-foreach="docs" t-as="o"> <t t-call="report.external_layout"> <div class="page"> <h2>Report title</h2> </div> </t> </t> </t> the standard rendering context provides a number of elements, the most important being: ``docs`` the records for which the report is printed ``user`` the user printing the report 'views/openacademy.xml', 'views/partner.xml', 'views/session_workflow.xml', 'reports.xml', ], # only loaded in demonstration mode 'demo': [ <openerp> <data> <report id="report_session" model="openacademy.session" string="Session Report" name="openacademy.report_session_view" file="openacademy.report_session" report_type="qweb-pdf" /> <template id="report_session_view"> <t t-call="report.html_container"> <t t-foreach="docs" t-as="doc"> <t t-call="report.external_layout"> <div class="page"> <h2 t-field="doc.name"/> <p>From <span t-field="doc.start_date"/> to <span t-field="doc.end_date"/></p> <h3>Attendees:</h3> <ul> <t t-foreach="doc.attendee_ids" t-as="attendee"> <li><span t-field="attendee.name"/></li> </t> </ul> </div> </t> </t> </t> </template> </data> </openerp> 'version': '0.1', # any module necessary for this one to work correctly 'depends': ['base', 'board'], # always loaded 'data': [ 'views/openacademy.xml', 'views/partner.xml', 'views/session_workflow.xml', 'views/session_board.xml', 'reports.xml', ], # only loaded in demonstration mode <?xml version="1.0"?> <openerp> <data> <record model="ir.actions.act_window" id="act_session_graph"> <field name="name">Attendees by course</field> <field name="res_model">openacademy.session</field> <field name="view_type">form</field> <field name="view_mode">graph</field> <field name="view_id" ref="openacademy.openacademy_session_graph_view"/> </record> <record model="ir.actions.act_window" id="act_session_calendar"> <field name="name">Sessions</field> <field name="res_model">openacademy.session</field> <field name="view_type">form</field> <field name="view_mode">calendar</field> <field name="view_id" ref="openacademy.session_calendar_view"/> </record> <record model="ir.actions.act_window" id="act_course_list"> <field name="name">Courses</field> <field name="res_model">openacademy.course</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> </record> <record model="ir.ui.view" id="board_session_form"> <field name="name">Session Dashboard Form</field> <field name="model">board.board</field> <field name="type">form</field> <field name="arch" type="xml"> <form string="Session Dashboard"> <board style="2-1"> <column> <action string="Attendees by course" name="%(act_session_graph)d" height="150" width="510"/> <action string="Sessions" name="%(act_session_calendar)d"/> </column> <column> <action string="Courses" name="%(act_course_list)d"/> </column> </board> </form> </field> </record> <record model="ir.actions.act_window" id="open_board_session"> <field name="name">Session Dashboard</field> <field name="res_model">board.board</field> <field name="view_type">form</field> <field name="view_mode">form</field> <field name="usage">menu</field> <field name="view_id" ref="board_session_form"/> </record> <menuitem name="Session Dashboard" parent="base.menu_reporting_dashboard" action="open_board_session" sequence="1" id="menu_board_session" icon="terp-graph"/> </data> </openerp> import xmlrpclib root = 'http://%s:%d/xmlrpc/' % (HOST, PORT) uid = xmlrpclib.ServerProxy(root + 'common').login(DB, USER, PASS) print "Logged in as %s (uid: %d)" % (USER, uid) # Create a new note sock = xmlrpclib.ServerProxy(root + 'object') args = { 'color' : 8, 'memo' : 'This is a note', 'create_uid': uid, } note_id = sock.execute(DB, uid, PASS, 'note.note', 'create', args) import functools import xmlrpclib HOST = 'localhost' PORT = 8069 DB = 'openacademy' USER = 'admin' PASS = 'admin' ROOT = 'http://%s:%d/xmlrpc/' % (HOST,PORT) # 1. Login uid = xmlrpclib.ServerProxy(ROOT + 'common').login(DB,USER,PASS) print "Logged in as %s (uid:%d)" % (USER,uid) call = functools.partial( xmlrpclib.ServerProxy(ROOT + 'object').execute, DB, uid, PASS) # 2. Read the sessions sessions = call('openacademy.session','search_read', [], ['name','seats']) for session in sessions: print "Session %s (%s seats)" % (session['name'], session['seats']) # 3.create a new session session_id = call('openacademy.session', 'create', { 'name' : 'My session', 'course_id' : 2, }) # 3.create a new session for the "Functional" course course_id = call('openacademy.course', 'search', [('name','ilike','Functional')])[0] session_id = call('openacademy.session', 'create', { 'name' : 'My session', 'course_id' : course_id, }) import json import random import urllib2 def json_rpc(url, method, params): data = { "jsonrpc": "2.0", "method": method, "params": params, "id": random.randint(0, 1000000000), } req = urllib2.Request(url=url, data=json.dumps(data), headers={ "Content-Type":"application/json", }) reply = json.load(urllib2.urlopen(req)) if reply.get("error"): raise Exception(reply["error"]) return reply["result"] def call(url, service, method, *args): return json_rpc(url, "call", {"service": service, "method": method, "args": args}) # log in the given database url = "http://%s:%s/jsonrpc" % (HOST, PORT) uid = call(url, "common", "login", DB, USER, PASS) # create a new note args = { 'color' : 8, 'memo' : 'This is another note', 'create_uid': uid, } note_id = call(url, "object", "execute", DB, uid, PASS, 'note.note', 'create', args) import jsonrpclib # server proxy object url = "http://%s:%s/jsonrpc" % (HOST, PORT) server = jsonrpclib.Server(url) # log in the given database uid = server.call(service="common", method="login", args=[DB, USER, PASS]) # helper function for invoking model methods def invoke(model, method, *args): args = [DB, uid, PASS, model, method] + list(args) return server.call(service="object", method="execute", args=args) # create a new note args = { 'color' : 8, 'memo' : 'This is another note', 'create_uid': uid, } note_id = invoke('note.note', 'create', args)
Run
Reset
Share
Import
Link
Embed
Language▼
English
中文
Python Fiddle
Python Cloud IDE
Follow @python_fiddle
Browser Version Not Supported
Due to Python Fiddle's reliance on advanced JavaScript techniques, older browsers might have problems running it correctly. Please download the latest version of your favourite browser.
Chrome 10+
Firefox 4+
Safari 5+
IE 10+
Let me try anyway!
url:
Go
Python Snippet
Stackoverflow Question