Show one2many field in a tree view of different model [Odoo] - python

I want to display all the taxes that are applied in a particular invoice and their amounts[in the tree view of a model account.invoice]
This is the output:
The column Tax Lines shows the ids of the taxes that are present in table account.invoice.tax (whereas I want to show their names and corresponding amount)
The model account.invoice has a field called tax_line_ids[Tax Lines] that contains the record of all the taxes on a invoice which is stored in a separate table account.invoice.tax, which in its own tree view looks like this:
I want to extract the tax name and its corresponding amount for it to reflect in account.invoice's tree view
Here's my python code which does'nt seem to work:
#api.one
def taxz(self):
tax_pool = self.pool.get("account.tax")
found_taxes = tax_pool.read(cr, uid, [tax_id,], ["tax_line_ids"], context)
found_tax = found_taxes[0] if found_taxes else None
tax_line_ids = found_tax["tax_line_ids"]
_logger.critical("context type: " + type(context))
_logger.critical("context content: " + str(context))
_logger.critical(tax_line_ids)
xml code for the view:
<field name="tax_line_ids" widget="many2many_tags" />

Try this:
class Invoice(models.Model):
_inherit = 'account.invoice'
tax_line_ids = fields.Many2many('account.invoice.tax',
'invoice_taxes',
'invoice_id',
'taxt_id',
'List of taxes',
compute='get_tax_list', store=True)
#api.depends('tax_line', 'tax_line.amount')
def get_tax_list(self):
for rec in self:
if rec.taxe_line:
rec.tax_line_ids = [(6,0,rec.tax_line.ids)]
else:
rec.tax_line_ids = [(5,0,0)]
But doing this will just show the list of taxes without amount in your tree view
if you want to show the amount you need to override the name_get method in account.invoice.tax
but this will affect all the x2many fields.
class AcountInvoiceTax(models.Model):
_inherit = 'account.invoice.tax'
#api.multi
def name_get(self):
res = []
for rec in self:
res.append((rec.id, rec.name +': '+ str(rec.amount)))
return res
If you don't want this then you need to change the type to Char and recompute the field
or create another model to save taxes and define name_get for that model.
As you can see this worked for me now if you still getting the keyErro you must be doing some thing wrong check your code for indentation inherit value... :

You can do add a char field just to use it in display in treeview :
class Invoice(models.Model):
_inherit = 'account.invoice'
tax_line_tree_view = fields.Char(compute='get_tax_list')
#api.multi
def get_tax_list(self):
tax_disp = ""
for rec in self:
if rec.taxe_line:
for tax in taxe_line:
tax_disp = tax_disp + "["+tax.name+"], "
rec.tax_line_tree_view = tax_disp[:-2] # [:-2] just to remove the last ', ' characters.
else:
rec.tax_line_tree_view = tax_disp
In your XML file where tree_view is located, add this field:
<field name="tax_line_tree_view " />
instead of tax_line_ids.
I hope that answer your question.

Related

Get a sale.order with an ID in other model [Odoo]

In the model account_move, I have a field called "invoice_origin" that have the ID of a sale order, but I need the entire sale.order. Both models are not related. How can I search and return the entire sale.order, in the move_account file?
I've tried something like this but it doesn't work
client_order_ref = fields.Char('get_sale_order().client_order_ref')
# Get a sale order with the invoice_origin field
def get_sale_order(self):
sale_order = self.env['sale.order'].search([
('name', '=', self.invoice_origin)
])
return sale_order
ERROR psycopg2.errors.UndefinedColumn: column
account_move.client_order_ref does not exist LINE 1:
...ier_move_reference" as "supplier_move_reference", "account_m...
You passed 'get_sale_order().client_order_ref' as first parameter to Char, Odoo will use it as a label for client_order_ref field and the get_sale_order function will not be called.
To compute the value of a field, use the compute parameter
Example:
client_order_ref = fields.Char(compute='get_sale_order')
#api.depends('invoice_origin')
def get_sale_order(self):
for move in self:
sale_order = self.env['sale.order'].search([
('name', '=', move.invoice_origin)
])
move.client_order_ref = sale_order.client_order_ref
Note that move lines are related to sale lines with sale_line_ids field

Add computed field to custom filter as stored field in ODOO

customer_count = fields.Char(string='Customer Count', compute='cust_count')
class ResBuilding(models.Model):
_name = "res.partner.building"
_description = "Customer BUilding"
_order = "id desc"
region_id = fields.Many2one('res.state.city', string='Region', required=True, ondelete='cascade')
city_id = fields.Many2one('city.covered.area', string='Area', required=True, ondelete='cascade')
name = fields.Char(string='Name', required=True, translate=True, copy=False)
image = fields.Binary(string="Building image")
latitude = fields.Char(string='Latitude')
customer_count = fields.Char(string='Customer Count', compute='cust_count', store=True)
longitude = fields.Char(string='Longitude')
active = fields.Boolean(string='Active', default=True, track_visibility='onchange')
partner_ids = fields.One2many('res.partner', 'building_id', string='Customer List', readonly=True)
#api.multi
#api.depends('partner_ids')
def cust_count(self):
for record in self:
count = self.env['res.partner'].search_count([('building_id', '=', record.id)])
record.customer_count = count
#api.multi
def name_get(self):
result = []
for route in self:
name = route.city_id.name + '-' + route.name
result.append((route.id, name))
return result
#api.multi
def write(self, vals):
res = super(ResBuilding, self).write(vals)
print(vals, self.id)
if vals.get('city_id'):
customers = self.env['res.partner'].search([('building_id', '=', self.id)])
for c in customers:
c.living_area = vals.get('city_id')
return res
#api.multi
#api.depends('latitude', 'longitude')
def on_change_location(self):
for li in self:
if li.latitude and li.longitude:
self.env.cr.execute("""update res_partner set location_latitude=""" + str(li.latitude) + """,
location_longitude=""" + str(li.longitude) + """where building_id=""" + str(
li.id))
return True
#api.multi
def write(self, vals):
res = super(ResBuilding, self).write(vals)
self.on_change_region_id()
return res
#api.multi
#api.depends('region_id')
def on_change_region_id(self):
for li in self:
if li.region_id:
self.env.cr.execute(
"""update res_partner set city_id=""" + str(li.region_id.id) + """where building_id=""" + str(
li.id))
return True
I want find the customer count in a specific building based on building id. and want The coustomer_count field to be added to the add custom filter
by this code ,i am getting count correctly . But the field customer_count does not appear in custom filter
when i add store=True, the field is coming in custom filter but the count is coming as 0.
Your code is not correct, even with storing the field.
customer_count = fields.Char(
string='Customer Count', compute='cust_count', store=True)
#api.multi
#api.depends()
def cust_count(self):
for record in self:
count = self.env['res.partner'].search_count([('building_id', '=', record.id)])
record.customer_count = count
Always use for each loops in compute methods, because in case of multi relational fields using your building model or just by presenting this computed field in a list of your building model will lead to a multi recordset behind self.
But that's not all. There should be a possibility to trigger the recomputation of the field and if easy to do using depends. Right now i don't see any easy possibility, because i don't know all your relations and workflows. Without storing the field you probably don't need that, but it would work there too.
So what to do to trigger a recomputation? Just work up from the other site of the relation: res.partner. Override it's write, create and unlink method to trigger the recomputation "manually".
class ResPartner(models.Model):
_inherit = "res.partner"
#api.multi
def write(self, values):
old_buildings = self.mapped('building_id')
res = super(ResPartner, self).write(values)
if 'building_id' in values:
new_buildings = self.mapped('building_id')
trigger_buildings = old_buildins | new_buildings
trigger_buildings.cust_count()
return res
#api.model
def create(self, values):
partner = super(ResPartner, self).create(values)
partner.building_id.cust_count()
return partner
#api.multi
def unlink(self):
buildings = self.mapped('building_id')
res = super(ResPartner, self).unlink()
buildings.cust_count()
return res
Another way is to use a one2many field on your building model in relation to res.partner and fill depends with it, like depends('partner_ids'). But in my experience one2many fields in such and lot of other situations tend to lead to bad performance.

Odoo: How to create many records in Transient.Model?

This code only creates one record. What is wrong?
class PartnerTagCreate(models.TransientModel):
""" Choose tags to be added to partner."""
_name = 'partner.tags.create'
_description = __doc__
market_id = fields.Many2one('partner.tags', string='Market Tag')
application_id = fields.Many2one('partner.tags', string='Application Tag')
partner_id = fields.Integer()
#api.multi
def create_contact_tag(self):
for record in self.env['sale.order.line'].browse(self._context.get('active_ids', [])):
vals = {}
vals['partner_id'] = record.order_partner_id
self.write(vals)
return True
I need this function to create one record for each order_partner_id I selected before opening the wizard...
How to achieve that?
Here my new code (function) ...
def create_contact_tag(self):
sale_order_line_ids = self.env['sale.order.line'].browse(self._context.get('active_ids', []))
for partner in sale_order_line_ids:
values = {}
values['partner_id'] = partner.order_partner_id
self.create(values)
return {}
This creates one record for marketing_id and/or application_id and dedicated records for each partner_id in the record.
You use the 'create' method to create new records; this is the same for TransientModel as for the persistent Model.
So, replace
self.write(vals)
by
self.create(vals)
and you should be fine.

Loop through record lines method - Odoo v8

I have this method:
#api.onchange('qty', 'consumed_qty')
def _remaining_func(self):
for qty in self.isbn:
if self.qty or self.consumed_qty:
self.remaining_qty = self.qty +(-self.consumed_qty)
But I need it to loop through records of my line (One2many field), right now, if I add just one record it works just fine, but if I add two or more, it throws the Expected singleton error.
So, how can I loop with this method?
I have added the for qty in self.isbn but with no success.
This is the class where this method is declared:
class bsi_production_order_lines(models.Model):
_name = 'bsi.production.order.lines'
production_order = fields.Many2one('bsi.production.order', string="Production Orders")
isbn = fields.Many2one('product.product', string="ISBN", domain="[('is_isbn', '=', True)]")
qty = fields.Float(string="Quantity")
consumed_qty = fields.Float(string="Consumed quantity")
remaining_qty = fields.Float(string="Remaining quantity", compute="_remaining_func")
Any ideas?
you could try:
#api.onchange('qty', 'consumed_qty')
def _remaining_func(self):
for s in self:
for qty in s.isbn:
if s.qty or s.consumed_qty:
s.remaining_qty = s.qty +(-s.consumed_qty)
I hope this help you.

Read product qty on lines and on warehouses

I need to check in my lines, the products I have, their respective quantities, and know what is the availability of such products in warehouses, the stock.move and stock.picking do something like that, but it's old api, I need a custom method.
This is my method:
class bsi_production_order(models.Model):
_name = 'bsi.production.order'
name = fields.Char('Reference', required=True, index=True, copy=False, readonly='True', default='New')
date = fields.Date(string="Production Date")
production_type = fields.Selection([
('budgeted','Budgeted'),
('nonbudgeted','Non Budgeted'),
('direct','Direct Order'),
], string='Type of Order', index=True,
track_visibility='onchange', copy=False,
help=" ")
notes = fields.Text(string="Notes")
order_lines = fields.One2many('bsi.production.order.lines', 'production_order', states={'finished': [('readonly', True)], 'cancel': [('readonly', True)]}, string="Order lines", copy=True)
print_orders = fields.One2many('bsi.print.order', 'production_orders', string="Print Orders")
warehouse_quantity = fields.Char(compute='quantity', string='Quantity per warehouse')
class bsi_production_order_lines(models.Model):
_name = 'bsi.production.order.lines'
production_order = fields.Many2one('bsi.production.order', string="Production Orders")
isbn = fields.Many2one('product.product', string="ISBN", domain="[('is_isbn', '=', True)]")
qty = fields.Integer(string="Quantity")
consumed_qty = fields.Float(string="Consumed quantity")
remaining_qty = fields.Float(string="Remaining quantity")
I need to check from bsi.production.order on the order_lines One2many field, the isbn which is a product, how much of it is available on all the locations of the system, also, compare it to the qty field, so, from there I can go to another state on the object.
Think about the stock.picking or stock.move objects. It's basically the same logic.
So far, I've tried this method, to check if there is any line on the One2many object.
#api.multi
#api.depends('order_lines', 'order_lines.isbn')
def checkit(self):
#actual_stock = self.env['product.product'].browse(qty_available)
for record in self:
if self.order_lines:
for line in self.order_lines:
if line.isbn:
return line.isbn
else:
raise Warning(('Enter​ ​at least​ ​1​ ​ISBN to produce'))
This works so far, to check whether there is a isbn on the line, or not, I'll need to also check if there is enough on warehouse to make the calculation, and if there is, then proceed to the next stage, I'm only tuck on the stock.location part.
I've checked some other modules on stock management OCA repo, although there are similar routines, I couldn't find something really suitable to this.
There is this method, which seems quite likely what I need:
#api.multi
#api.depends('order_lines', 'order_lines.isbn')
def quantity(self):
for record in self:
warehouse_quantity_text = ''
isbn = self.env['product.product'].sudo().search([('product_tmpl_id', '=', record.id)])
if isbn:
quant_ids = self.env['stock.quant'].sudo().search([('isbn','=',isbn[0].id),('location_id.usage','=','internal')])
t_warehouses = {}
for quant in quant_ids:
if quant.location_id:
if quant.location_id not in t_warehouses:
t_warehouses.update({quant.location_id:0})
t_warehouses[quant.location_id] += quant.qty
tt_warehouses = {}
for location in t_warehouses:
warehouse = False
location1 = location
while (not warehouse and location1):
warehouse_id = self.env['stock.warehouse'].sudo().search([('lot_stock_id','=',location1.id)])
if len(warehouse_id) > 0:
warehouse = True
else:
warehouse = False
location1 = location1.location_id
if warehouse_id:
if warehouse_id.name not in tt_warehouses:
tt_warehouses.update({warehouse_id.name:0})
tt_warehouses[warehouse_id.name] += t_warehouses[location]
for item in tt_warehouses:
if tt_warehouses[item] != 0:
warehouse_quantity_text = warehouse_quantity_text + ' ** ' + item + ': ' + str(tt_warehouses[item])
record.warehouse_quantity = warehouse_quantity_text
But it doesn't works since it needs a field, also, I think it's far complex, there must be an easier way to do this checking.
In a nutshell: I need to check the quantities on the system, compare it to every isbn (product) on the line, which it'll be the qty field, if not enough, do nothing, if there is, then pass to the next state.
First if you want to check if the data is correct use #api.constrains not #api.depends, #api.depends if for computing.
From what is see isbn is many2one to product.product so just make that field required and check if order_lines is empty or not.
#api.constrains('order_lines', 'order_lines.isbn')
def checkit(self):
#actual_stock = self.env['product.product'].browse(qty_available)
for record in self:
# inside the loop use record not self
if self.order_lines:continue # if the order_lines contains one record go back and check the second record
# no need for other instruction because if the field is empty this will full
# another thing if you return the program will exit the function but you only
# checked one record what if someone user write with mutliple record
else: # here order_line is empty
raise Warning(('Enter? ?at least? ?1? ?ISBN to produce'))
but if some how you need to keep it unrequired i think will be much faster.
#api.constrains('order_lines', 'order_lines.isbn')
def checkit(self):
for record in self:
# inside the loop use record not self
if self.order_lines:
found_isbn = False
for line in self.order_lines:
if line.isbn:
found_isbn = True
break # no need to check other lines.
if not found_isbn: # after the looping the lines check if the isbn is found
raise Warning(('Enter at least one ISBN to produce'))
else: # here order_line is empty
raise Warning(('Enter? ?at least? ?1? ?ISBN to produce'))
and about the the quantity i didn't understand exactly what you need but i think this answer will help you a lot.
how to get available quantity of Lot number
and what you need to do is something like this.
if you want just to show a warning to user and don't stop him from working use onchange
#api.onchange('order_lines.qty')
def check_quantity(self):
if self.order_lines:
for line in rec.order_lines:
if line.qty > line.isbn.qty_available:
# return warning or validation error if it's restricted .
return {'warning': {
'title': _('Warning'),
'message': _('Quantity is invalid.')
}
but if this operation is restricted and should not be saved in database use constrains:
#api.constrains('order_lines.qty')
def check_quantity(self):
for rec in self:
if rec.order_lines:
for line in rec.order_lines:
if line.qty > line.isbn.qty_available:
# raise validation error to user .

Categories