I am using Odoo 10-e. I created a custom class for order
class Order(models.Model):
_name = 'amgl.order'
_description = 'Use this class to maintain all transaction in system.'
name = fields.Char(string='Name',readonly=True)
order_line = fields.One2many('amgl.order_line', 'order_id', string='Order Lines')
total_qty = fields.Float(string='Total Expected')
total_received_qty = fields.Float(string='Total Received Quantity')
customer_id = fields.Many2one('amgl.customer', string='Customers', required=True)
is_pending = fields.Boolean()
date_opened = fields.Datetime('Date Opened', required=True)
date_received = fields.Datetime('Date Received')
I also created a view for this class which show all records in tree view . Now i want to create another view named 'Pending Orders' in which i want to show all order where is_pending is true. I am new maybe that's why i am unable to find any example in Odoo Code base.
For this you don't need to create a new view just create a new menu and action and filter the records using domain.
<record id="action2_...." model="ir.actions.act_window" >
<field name="name"> Action Title </field>
....same as the first action...
<field name="res_model">your.model</fiel>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('is_pending', '=', True)] </field>
</record>
<menuitem ..... action="action2_.." />
NB: action can have properties like domain ,context, view_id, search_view_id, view_ids ... etc best way to learn is read about them and see the code in odoo.
Related
So I'm making this localization kind of module for Lebanon and am currently doing the location bit.
The intended functionality is to have the choices of governate, district and region restrict the choices the user can make by filtering out the irrelevant options.
Here are the models:
# -*- coding: utf-8 -*-
from odoo import models, fields, api
# Base model
class LebaneseLocation(models.Model):
_inherit = "res.partner"
governate_id = fields.Many2one("lebanon.governate", "Governate")
district_id = fields.Many2one("lebanon.district", "District")
region_id = fields.Many2one("lebanon.region", "Region")
# Child models with the necessary relations
class Governate(models.Model):
_name = "lebanon.governate"
name = fields.Char()
child_districts = fields.One2many("lebanon.district", "parent_governate",
"Child Districts")
class District(models.Model):
_name = "lebanon.district"
name = fields.Char()
parent_governate = fields.Many2one("lebanon.governate", "Parent Governate")
child_regions = fields.One2many("lebanon.region", "parent_district",
"Child Regions")
class Region(models.Model):
_name = "lebanon.region"
name = fields.Char()
parent_district = fields.Many2one("lebanon.district", "Parent District")
and the view file:
<?xml version="1.0"?>
<odoo>
<record id="view_res_partner_extended_location" model="ir.ui.view">
<field name="name">Contacts Custom</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<field name="category_id" position="after">
<field name="governate_id" domain="[('child_districts','=?','district_id'),('child_districts.child_regions','=?','region_id')]"/>
<field name="district_id"/>
<field name="region_id"/>
</field>
</field>
</record>
</odoo>
And all the data was inputed via two data files, one makes the records without relations and the other updates the same records with relations.
I was testing things out with the Governate domain and it just kept returning nothing.
Is there some way to check what exactly these parameters i'm evaluating are returning? That would help a lot with debugging.
Edit: Here's a screenshot from Studio's menu for the domain, in case that's at all relevant for debugging (I avoid Studio like a plague usually)
Domain Image
I solved my issue by forgoing the XML approach and setting the domains using the onchange api call:
#api.onchange('governate_id', 'district_id', 'region_id')
def update_domains(self):
governate = self.governate_id.name
district = self.district_id.name
region = self.region_id.name
result = {}
result['domain']={
'governate_id':[('child_districts.name','=?', governate),
('child_districts.child_regions.name', '=?', region)],
'district_id':[('parent_governate.name', '=?', governate),
('child_regions.name', '=?', region)],
'region_id':[('parent_district.name', '=?', district),
('parent_district.parent_governate.name', '=?', governate)]
}
return result
I have create a list view with the help of psql query with _auto = False. So there is not model registered against this. Now i want to customize this click event on the records that whenever a user click on any records i want to pass an order_id and then redirect user to that particular order detail screen.
Edit
View
<odoo>
<data>
<record id="amgl_dashboard_tree" model="ir.ui.view">
<field name="name">Dashboard</field>
<field name="model">amgl.dashboard</field>
<field name="arch" type="xml">
<tree default_order="state desc" decoration-bf ="state in ('expecting','pending','completed')" decoration-info="state=='expecting'" decoration-danger="state=='pending'" decoration-succes="state=='completed'" string="Dashboard" create="false" edit="false">
<field name="order_id" invisible="1"/>
<button name="view_record" type="object" string="View Record" custom="click" class="oe_highlight"/>
<field name="first_name"/>
<field name="last_name"/>
<field name="account_number"/>
<field name="product"/>
<field name="quantity"/>
<field name="total_weight"/>
<field name="state"/>
</tree>
</field>
</record>
</data>
</odoo>
.Py
class Dashboard(models.Model):
_name = 'amgl.dashboard'
_auto = False
#api.multi
def view_record(self):
print self
#api.model_cr
def init(self):
tools.drop_view_if_exists(self._cr, 'dashboard')
self._cr.execute("""
CREATE or REPLACE VIEW amgl_dashboard AS (
SELECT
row_number() OVER () AS id,
c.name AS first_name,
c.last_name AS last_name,
c.account_type AS account_type,
c.account_number AS account_number,
(select name from amgl_products where id = ol.products) AS product,
ol.quantity AS quantity,
(CASE
WHEN (select weight_unit from amgl_products where id = ol.products) = 'oz'
THEN
(select weight_per_piece from public.amgl_products where id = ol.products) * ol.quantity
WHEN (select weight_unit from amgl_products where id = ol.products) = 'gram'
THEN
((select weight_per_piece from public.amgl_products where id = ol.products) / 28.34952) * ol.quantity
WHEN (select weight_unit from amgl_products where id = ol.products) = 'pounds'
THEN
((select weight_per_piece from amgl_products where id = ol.products) * 16) * ol.quantity
WHEN (select weight_unit from amgl_products where id = ol.products) = 'kg'
THEN
((select weight_per_piece from amgl_products where id = ol.products) / 0.02834952) * ol.quantity
ELSE 0.0
END) AS total_weight,
o.state AS state,
o.id AS order_id
FROM amgl_order AS o
INNER JOIN amgl_customer AS c ON c.id = o.customer_id
INNER JOIN amgl_order_line AS ol ON ol.order_id = o.id
)""")
name = fields.Char()
first_name = fields.Char(string="First Name")
last_name = fields.Char(string="Last Name")
account_type = fields.Char(string="Account Type")
account_number = fields.Char(string="Account Number")
product = fields.Char(string="Product")
quantity = fields.Float(string="Quantity")
total_weight = fields.Float(string="Total Weight")
state = fields.Selection([('expecting', 'Expecting'), ('pending', 'Pending'),
('completed', 'Completed'), ('waiting', 'Waiting For Approval')],
'Status', default='expecting')
order_id = fields.Integer(string="Order Id")
Action
<record id="amgl.dashboard_action_window" model="ir.actions.act_window">
<field name="name">Dashboard</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">amgl.dashboard</field>
<field name="view_mode">tree</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
<!-- Add Text Here -->
</p><p>
<!-- More details about what a user can do with this object will be OK -->
</p>
</field>
</record>
Debugger
Self._context
Self
I think there are a couple of ways this could possibly work. I'm not 100% certain either of them will, but I'll give you the general ideas.
XML Only Solution
George has the main idea correct - you need to create an action to manage what you're wanting to do.
The important distinction is that you seem to want the list view to display one model (amgl.dashboard) but the form view to display another (sale.order)
Normally, I would suggest you create a button and display it in the list view to take the user directly to the sales order form. However, you may also be able to use a standard act_window action with the help of src_model.
Example from core:
Odoo uses the button below to invoke the action, which results in the user being able to go to the stock.location form view and go to the product.product list. You are essentially wanting to do the opposite of this with different models and without the button.
<button string="Products"
class="oe_stat_button"
icon="fa-filter" name="%(act_product_location_open)d" type="action"
context="{'location_id': active_id}"
/>
<act_window
id="act_product_location_open"
name="Products"
src_model="stock.location"
res_model="product.product"
context="{'location': active_id,
'search_default_real_stock_available': 1,
'search_default_virtual_stock_available': 1,
'search_default_virtual_stock_negative': 1,
'search_default_real_stock_negative': 1}"/>
Python Solution:
Assuming your view_record method is being called already, you can return the "View Sales Order form" action directly from the method itself.
# Make sure these imports are called in your file
# If you are on Odoo 9 or earlier, you must use openerp instead of odoo
from odoo import _
from odoo.exceptions import ValidationError
class Dashboard(models.Model):
_name = 'amgl.dashboard'
_auto = False
#api.multi
def view_record(self):
"""Return a Window Action to view the Sales Order form"""
self.ensure_one()
action = self.env.ref('sale.action_orders')
form = self.env.ref('sale.view_order_form', False)
if not (action or form):
raise ValidationError(_("Sales Orders Action or Form not found!"))
return {
'name': action.name,
'help': action.help,
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'views': [(form_id.id, 'form')]
'res_model': action.res_model,
'res_id': self._context.get('id'),
'target': 'current',
'context': {},
}
When you click a record on a listview an action is executed that moves you from the listview to a `formview.
There always exists a default action but if you want to pass some extra parameters you have to define your own. For a concrete example take a look at the sale.action_quotations action. This action moves the user from view1 to view2 (list to form for example)
https://github.com/OCA/OCB/blob/10.0/addons/sale/views/sale_views.xml#L518
<record id="action_quotations" model="ir.actions.act_window">
...
<field name="context">{'hide_sale': True}</field>
...
</record
This action is responsible for moving the user in Quotations between views. See how the context is passed. You only need tree,form on the view_mode here.
I have a model(modelA) with one2many field related to another model(modelB) and one of the fields in modelB is a category field, which is a many2one field. The requirement is to have a one2many field displayed for each category. So if there are 2 categories named 'category1' and 'category2', the form view of modelA should have 2 one2many fields, one which displays records of having category1 and another for category2(which could possibly done using domain).
For eg modelA and modelB has the following structure.
class classA(models.Model):
_name = 'modelA'
modelA_one2manyfield = fields.One2many('modelB', 'modelB_many2onefield')
class classB(models.Model):
_name = 'modelB'
name = fields.Char()
category = fields.Many2one('modelC')
modelB_many2onefield = fields.Many2one('modelA')
How would i go about implementing a form view for modelA so that for each category(which can be added by the user, hence there can be of any number of categories) there is a seperate one2many field.
What you are asking take a lot of time to give a very good answer one of the way that i think you need to try is override the fields_view_get because this is the method that retreive the view and here you can change the arch field to add a costum field take a look at this tutorial :
Tutorial for dynamic view
but i think you will have a problem, because even when you put the domain on the one2many field in XML, odoo will not filter
the record when the loading happen on the view :
<!-- here all record are shown but the expected behavior is the one2many should be empty -->
<field name="one2many_field_name" readonly="1" nolabel="1" domain="[('id', '=', False)]">
but when i add this field to the python declaration
# here no record will be shown on the view and that's what was expected
one2many_field_name = fields.One2many(..., domain=[('id', '=', False)])
so the question adding one2many field to arch via fields_view_get is easy but the problem is filtring data !!
It's technically not possible. Because you can't have 2 times the same field in the same view.
But you can create a specific widget to showing what you want. How you can see in the timesheet view (My Current timesheet menu).
This is a little tutorial to created a widget.
https://www.odoo.com/documentation/10.0/howtos/web.html#widgets-basics
This not an answer but you can say a tutorial example of dynamic view :
modul structur:
->dynamic_view
--> __ini__.py
--> models.py
--> views.xml
--> __manifest__.py
__manifest__.py :
# -*- coding: utf-8 -*-
{
'name' : 'Dynamic view',
'version' : '1.0',
'summary': 'Tutorial for Dynamic view',
'sequence': 30,
'description': """
This Module is for showing that you can update the code of the view
when it's called and even create new field without having to use python
code at all
""",
'category': 'StackOverFlow',
'depends' : ['base_setup',],
'data': [
'views.xml'
],
'installable': True,
'application': True,
'auto_install': False,
}
__init__.py :
# -*- coding: utf-8 -*-
from . import models
models.py :
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class Person(models.Model):
_name = "training.person"
name = fields.Char("Full name")
class Car(models.Model):
_name = "training.car"
name = fields.Char("Car name")
mark_id = fields.Many2one(comodel_name="training.mark", string="Mark")
owner_id = fields.Many2one(comodel_name="training.person", string="Owner")
person_view_id = "dynamic_view.dgapr_form_person"
# here default arch value body in the view contains only
# name field but as we create new mark we add others field
person_view_arch = """
<group>
<field name="name"/>
</group>
"""
class Mark(models.Model):
_name = "training.mark"
name = fields.Char("Mark")
#api.model
def create(self, values):
"""
when we create a category we add one2many field to person view
TODO: when we unlink a category we need to remove the one2many
name of field is : x_mark_{id of deleted record}
"""
rec_id = super(Mark, self).create(values)
o2m_field = {
# fields created using orm method must start with x_
"name": "x_mark_%s"% rec_id.id,
"field_description": "Mark %s" % rec_id.name,
"ttype": "one2many",
"relation": "training.car",
"relation_field": "owner_id",
"stored": True,
"domain": "[('mark_id','=', %s)]"%rec_id.id,
"model_id": self.env.ref("dynamic_view.model_training_person").id,
}
# add on2many field to ir.model.fields
self.env["ir.model.fields"].create(o2m_field)
self.update_arch()
return rec_id
def update_arch(self):
"""
when ever we create or delete a mark record
we need to update the the view to add new one2many field
if we want to hide the one2many field in view that don't have
any record we should create compute field to use attrs features
"""
view_id = self.env.ref(person_view_id)
o2m_fields_ids = self.env['ir.model.fields'].search(
[
('model_id', '=', self.env.ref("dynamic_view.model_training_person").id),
('ttype', 'like', 'one2many'),
('relation_field', 'like', 'owner_id')
])
o2many_arch = ""
for o2m_id in o2m_fields_ids:
o2many_arch = o2many_arch + """
<group col="1" string="%s">
<field name="%s" noloable="1" />
</group>
""" % (o2m_id.field_description, o2m_id.name,)
arch_begin = """
<form>
<sheet>
"""
arch_close = """
</sheet>
</form>
"""
arch_body = person_view_arch + o2many_arch
new_arch = arch_begin + arch_body + arch_close
# update the arch of the view in database
view_id.arch = new_arch
views.xml:
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data>
<record id="dgapr_form_car" model="ir.ui.view">
<field name="name">car.form</field>
<field name="model">training.car</field>
<field name="arch" type="xml">
<form >
<sheet>
<group>
<field name="name"/>
<field name="mark_id"/>
<field name="owner_id"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="dgapr_action_car" model="ir.actions.act_window">
<field name="name">Cars</field>
<field name="res_model">training.car</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_root_training" name="Training"/>
<menuitem id="menu_ch_car" name="Cars" parent="menu_root_training" action="dgapr_action_car"/>
<record id="dgapr_form_person" model="ir.ui.view">
<field name="name">dgapr.form.person</field>
<field name="model">training.person</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name"/>
</group>
</sheet>
</form>
</field>
</record>
</data>
</odoo>
i found out that you can create field using ORM method even compute field. i think creating a widget is better but good to know that wen can create costum fields .
Hope this helps you
Note i didn't create a menu for person record but you can see the view by clicking on the owner_id in the car form if the new one2many field not shown just refresh the page.
Need to store default value on qty in M.sqr and square meter like quantity on hand showing default entered value.
when i click on update button of quantity on Hand from product inventory page
then it should show me a previous entered value.
class stock_change_product_qty(osv.osv):
_inherit = 'stock.change.product.qty'
_columns = {
'new_quantity' : fields.float('Qty in Boxes'),
'squ_meter': fields.related('product_id','squ_meter', type='float', relation='product.product', string='Square Meter'),
'qty_char': fields.float('Qty in M.sqr', compute='_compute_qty_char'),
}
#api.depends('new_quantity', 'squ_meter')
def _compute_qty_char(self):
for record in self:
record.qty_char = record.new_quantity * record.squ_meter
view.xml
<field name="name">update_product_quantity inherit</field>
<field name="model">stock.change.product.qty</field>
<field name="inherit_id" ref="stock.view_change_product_quantity"/>
<field name="arch" type="xml">
<xpath expr="//field[#name='new_quantity']" position="attributes">
<attribute name="string">Qty in Boxes</attribute>
</xpath>
<xpath expr="//field[#name='new_quantity']" position="replace">
<field name="new_quantity"/>
<field name="squ_meter"/>
<field name="qty_char"/>
</xpath>
</field>
</record>
A solution is to use a function as default value for your field:
odoo V8
from openerp import api, fields, models
class stock_change_product_qty(models.Model):
_inherit = 'stock.change.product.qty'
new_quantity = fields.Float(string='Qty in Boxes', default=1.0),
squ_meter = fields.Float(related='product_id.squ_meter', string='Square Meter', default=1.0),
qty_char = fields.Float('Qty in M.sqr', compute='_compute_qty_char'),
}
#api.one
#api.depends('new_quantity', 'squ_meter')
def _compute_qty_char(self):
for record in self:
record.qty_char = record.new_quantity * record.squ_meter
odoo V7
def _your_function(self, cr, uid, context=None):
your code here
...
...
...
return field_value
_defaults = {
'your_field' : _your_function,
}
#api.onchange
This decorator will trigger the call to the decorated function if any of the fields specified in the decorator is changed in the form:
#api.onchange('fieldx')
def do_stuff(self):
if self.fieldx == x:
self.fieldy = 'toto'
In previous sample self corresponds to the record currently edited on the form. When in on_change context all work is done in the cache. So you can alter RecordSet inside your function without being worried about altering database. That’s the main difference with #api.depends
At function return, differences between the cache and the RecordSet will be returned to the form.
i am having some problems when i try to add a field in the parent view.
The class is:
class VademecumFraccionamiento(models.Model):
_name = 'farmacia.vademecum_fraccionamiento'
_inherits={
'farmacia.vademecum': 'vademecum_id'
}
hijo = fields.Many2one('farmacia.vademecum_fraccionamiento', string="Artículo hijo", index=True)
vademecum_id = fields.Many2one('farmacia.vademecum', string='Artículo Padre', required=True, ondelete='cascade', index=True)
The xml is:
<record model="ir.ui.view" id="farmacia_vademecum_fraccionamiento_form_view">
<field name="name">farmacia_vademecum_fraccionamiento.form</field>
<field name="model">farmacia.vademecum</field>
<field name="inherit_id" ref="farmacia_vademecum.farmacia_vademecum_form_view"/>
<field name="arch" type="xml">
<xpath expr="//page[#string='lalala']" position="after">
<page string="Fracc">
</page>
</xpath>
<xpath expr="//page[#string='Fracc']" position="inside">
<group>
<field name="vademecum_id">
</field>
</group>
</xpath>
</field>
</record>
The error is:
Error details:
The field vademecum_id not exists
I don't know how to solve that.
Thanks in advance
You should refer addons/product/product_view.xml for more help, in which you will get all the answers of your questions related to inheritance.
I would change the code to:
_columns = {
'hijo' : fields.Many2one('farmacia.vademecum_fraccionamiento', string="Artículo hijo", index=True),
'vademecum_id' : fields.Many2one('farmacia.vademecum', string='Artículo Padre', required=True, ondelete='cascade', index=True),
}
This will add the fields to your model
There are two concepts in odoo for field inheritance.
_inherit : can be used in case you want to extend existing model.
Example: adding birth date field in res.partner model
class res_partner(models.Model):
_inherit = 'res.partner'
birth_date = fields.Date('Birthdate')
_inherits : can be used in case you want to adept module field in current model.
Example: Using customer fields in student model,
class Student(models.Model):
_name = 'stundent.student'
_inherits = {'res.partner': partner_id}
partner_id = fields.Many2one('res.partner', 'Partner')
after adding a partner_id field in your model you can use all the fields of partner in xml view of student form & tree.
Hope this helps.