I need to override the create method in my model on odoo 10 :
in my module i have Three Models :
Asset With
validated = fields.Boolean("Is validated")
survey2_ids = fields.One2many('mymodule.survey2', 'asset_id', string='Survey2')
Survey2 with :
name = fields.Char()
asset_id = fields.Many2one('asset.asset', description='Asset')
survey1_id = fields.Many2one('mymodule.survey1', description="Survey1")
description = fields.Text(description="description")
Survey1 with :
name = fields.Char(description="Name")
ok = fields.Boolean("Is ok")
description = fields.Text()
The goal in here is when creating a new asset, and if validated = True: all records in mymodule.survey1 with ok==True should be copied in survey2_ids, i tried this function but it doesn't seem to be working:
#api.model
def create(self, vals):
survey1_ids = self.env['mymodule.survey1'].search([('ok', '=', True)])
if self.validated:
for rec in survey1_ids:
vals['survey2_ids'] = [(0, False, {'asset_id': self.id, 'survey2_id': rec.id,'name':rec.name,'description':})]
return super(asset_asset, self).create(vals)
Any help will be aappreciated
There are two problems in your code :
Create is kind of a "class method" (it is tied to the model, no to the record). So when you ask for the value of self.validated, this will always be false because self is not the record you're creating, it's the model. You should check vals.get('validated') instead. Or create the record before-hand and use it instead of self (in my example, res in the newly created record).
You're not really copying survey 1 into survey 2. You just have to create survey 2 using the data in survey 1.
The solution that I think is best :
#api.model
def create(self, vals):
res = super(asset_asset, self).create(vals)
if vals.get('validated'):
survey1_ids = self.env['mymodule.survey1'].search([('ok', '=', True)])
for s in survey1_ids:
v = {
'name': s.name,
'description': s.description,
'survey1_id': s.id,
'asset_id': res.id
}
self.env['mymodule.survey2'].create(v)
return res
Assuming that there are no errors in the logs, you are not getting what you intended to do. Once the code has executed, you are only getting 1 survey attached to the asset.
This is because inside the create function you wrote:
vals['survey2_ids'] = [(0, False, {'asset_id': self.id, 'survey2_id': rec.id,'name':rec.name,'description':})]
This will override the survey2_id in the vals each and every time in the for loop.
What you should do here is:
survey_2_list = []
for rec in survey1_ids:
survey_2_list.append((0, False, {'asset_id': self.id, 'survey2_id': rec.id,'name':rec.name,'description':rec.description}))
vals['survey2_ids'] = survey_2_list
Try the following:
#api.model
def create(self, vals):
survey_2_list = []
if self.validated:
survey1_ids = self.env['mymodule.survey1'].search([('ok', '=', True)])
if survey1_ids:
for rec in survey1_ids:
values = {
'asset_id': self.id,
'survey2_id': rec.id,
'name':rec.name,
'description':rec.description,
}
survey_2_list.append((0, False, values))
vals['survey2_ids'] = survey_2_list
return super(asset_asset, self).create(vals)
Related
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.
In Graphene-Django and GraphQL I am trying to create a resolve_or_create method for nested data creation inside my mutations.
I'm trying to pass a dictionary with the input from the user as a **kwarg to my resolve_or_create function, and though I can see "location" in the variable watcher (in VSCode), I continuously am getting the error 'dict' object has no attribute 'location'
Here's my resolve_or_create method:
def resolve_or_create(*args, **kwargs):
input = {}
result = {}
input.location = kwargs.get('location', None)
if input.location is not None:
input.location = Location.objects.filter(pk=input.location.id).first()
if input.location is None:
location = Location.objects.create(
location_city = input.location.location_city,
location_state = input.location.location_state,
location_sales_tax_rate = input.location.location_sales_tax_rate
)
if location is None:
return None
result.location = location
return result
and my CreateCustomer definition, where this method is being called
class CreateCustomer(graphene.Mutation):
class Arguments:
input = CustomerInput(required=True)
ok = graphene.Boolean()
customer = graphene.Field(CustomerType)
#staticmethod
def mutate(root, info, input=None):
ok = True
resolved = resolve_or_create(**{'location':input.customer_city})
customer_instance = Customer(
customer_name = input.customer_name,
customer_address = input.customer_address,
customer_city = resolved.location,
customer_state = input.customer_state,
customer_zip = input.customer_zip,
customer_email = input.customer_email,
customer_cell_phone = input.customer_cell_phone,
customer_home_phone = input.customer_home_phone,
referred_from = input.referred_from
)
customer_instance.save()
return CreateCustomer(ok=ok, customer=customer_instance)
Here is an example mutation that would create a new customer with an existing location
mutation createCustomer {
createCustomer(input: {
customerName: "Ricky Bobby",
customerAddress: "1050 Airport Drive",
customerCity: {id:1},
customerState: "TX",
customerZip: "75222",
customerEmail: "mem",
customerCellPhone: "124567894",
referredFrom: "g"
}) {
ok,
customer{
id,
customerName,
customerAddress,
customerCity {
id
},
customerState,
customerZip,
customerEmail,
customerCellPhone,
referredFrom
}
}
}
and here is an example mutation that would create a customer with a new location
mutation createCustomer {
createCustomer(input: {
customerName: "Ricky Bobby",
customerAddress: "1050 Airport Drive",
customerCity: {locationCity: "Dallas", locationState: "TX", locationSalesTaxRate:7.77},
customerState: "TX",
customerZip: "75222",
customerEmail: "mem",
customerCellPhone: "124567894",
referredFrom: "g"
}) {
ok,
customer{
id,
customerName,
customerAddress,
customerCity {
id
},
customerState,
customerZip,
customerEmail,
customerCellPhone,
referredFrom
}
}
}
So my question is two-fold.
First, how can I retrieve my passed in location dict from kwargs?
Second, is there a better way to be resolving and creating nested data than this? Would this be expected behavior in a best-practice GraphQL API?
I have also tried resolve_or_create(location=input.customer_city})
I realized my syntax for dictionary assignment and access was wrong.
Corrected function:
def resolve_or_create(*args, **kwargs):
input = {}
result = {}
candidate = None
input['location'] = kwargs['location']
input['customer'] = kwargs.get('customer', None)
input['technician'] = kwargs.get('tech', None)
if input['location'] is not None:
if 'id' in input['location']:
candidate = Location.objects.filter(pk=input['location']['id']).first()
result['location'] = candidate
if candidate is None:
result['location'] = Location.objects.create(
location_city = input['location']['location_city'],
location_state = input['location']['location_state'],
location_sales_tax_rate = input['location']['location_sales_tax_rate']
)
if result['location'] is None:
return None
return result
I would still like to discuss the most effective way to accomplish created and mutating nested data in graphene-django. Is there a DRYer way to achieve what I'm looking to do or something I'm missing?
I am trying to make a pop-up wizard with one selection with values from a compute function. As shown below. I am still unable to set the selection field values. Inside function _get_quotation_so I always get self.medical_quotation_id FALSE.
Is there a way to fill the selection field values? Maybe in the create function? Can anyone show me how?
class MedicalQuotationInvoiceWizard(models.Model):
_inherit = 'medical.quotation'
def compute_medical_quotation_so(self):
# import ipdb; ipdb.set_trace()
so = []
sos = self.medical_quotation_so_ids.search([('medical_quotation_id.id', '=', self.id)])
for record in sos:
so.append((record.id, record.name))
return so
#api.multi
def invoice_wizard(self):
# for record in self:
params={}
view_id=self.env['prescription.invoice.wizard']
params = {
'medical_quotation_id': self.id,
'invoice_version': self.invoice_version,
}
new = view_id.create(params)
return {
'type': "ir.actions.act_window",
'name': "Invoice Wizard",
'res_model': "prescription.invoice.wizard",
'view_type': "form",
'view_mode': "form",
'res_id': new.id,
'view_id': self.env.ref('medical_prescription.view_prescription_invoice_wizard', False).id,
'target': "new",
}
class PrescriptionInvoiceWizard(models.TransientModel):
_name = 'prescription.invoice.wizard'
def _get_prescription_invoice(self):
medical_quotation = self.env['medical.quotation']
return medical_quotation.compute_prescription_invoice()
invoice_version = fields.Selection(string="Invoice Version",
selection=lambda self: self._get_prescription_invoice())
logo = fields.Boolean("Company Logo")
paging = fields.Boolean("Paging")
medical_quotation_id = fields.Many2one(comodel_name='medical.quotation', string="Medical Quotation")
#api.model
def create(self, values):
# Override the original create function for the res.partner model
record = super(PrescriptionInvoiceWizard, self).create(values)
import ipdb; ipdb.set_trace()
medical_quotation = self.env['medical.quotation'].search([('id', '=', values['medical_quotation_id'])])
record['medical_quotation_id'] = medical_quotation
# Return the record so that the changes are applied and everything is stored.
return record
#api.depends('medical_quotation_id')
def _get_quotation_so(self):
# import ipdb; ipdb.set_trace()
medical_quotation = self.env['medical.quotation'].search([('id', '=', self.medical_quotation_id.id)]) <--- HERE self.medical_quotation_id ALWAYS FALSE
return medical_quotation.compute_medical_quotation_so()
medical_quotation_so_select = fields.Selection(string="SO",
selection=lambda self: self._get_quotation_so())
I actually solved this, but without using select field. I used many2one field then in the xml I put domain that filter using another field.
Thats it. If the python back end cannot do it. Then I guess the view front end will do it.
I have receiving this odoo error when I try to duplicate a set of record. I have inherited ['mail.thread', 'ir.needaction_mixin'] in the current class. I didn't found any solution online or myself or from odoo. Still stuck here around four days.
Can anybody have an idea on this? Currently I'm using Odoo 10.
Code added is below:
#api.model
def create(self, vals):
res = super(StaffKPI, self).create(vals)
fol = {}
fol['res_model'] = 'staff.kpi'
fol['res_id'] = res.id
fol['partner_id'] = res.name_id.partner_id.id
fol_id = self.env['mail.followers'].create(fol)
self._cr.execute(
'INSERT INTO mail_followers_mail_message_subtype_rel (mail_followers_id, mail_message_subtype_id) values (%s, %s)',
(fol_id.id, 2))
self._cr.execute(
'INSERT INTO mail_followers_mail_message_subtype_rel (mail_followers_id, mail_message_subtype_id) values (%s, %s)',
(fol_id.id, 1))
subtypes = self.env['mail.message.subtype'].search([('res_model', '=', 'staff.kpi')]).ids
if subtypes:
for i in subtypes:
self._cr.execute(
'INSERT INTO mail_followers_mail_message_subtype_rel (mail_followers_id, mail_message_subtype_id) values (%s, %s)',
(fol_id.id, i))
old_name = res.name
res.write({'name': ''})
res.write({'name': old_name})
return res
You should use mail.threads built-in functions to add followers. You should always avoid the use of direct queries!
Following code will cover the follower and subtype adding (i didn't understand the name writes at the end):
#api.model
def create(self, vals):
res = super(StaffKPI, self).create(vals)
subtype_ids = self.env['mail.message.subtype'].search(
[('res_model', '=', 'staff.kpi')]).ids
res.message_subscribe(
partner_ids=[res.name_id.partner_id.id],
subtype_ids=subtype_ids)
# other logic
return res
you can use this code:
class Followers(models.Model):
_inherit = 'mail.followers'
#api.model
def create(self, vals):
if 'res_model' in vals and 'res_id' in vals and 'partner_id' in vals:
dups = self.env['mail.followers'].search([('res_model', '=',vals.get('res_model')),
('res_id', '=', vals.get('res_id')),
('partner_id', '=', vals.get('partner_id'))])
if len(dups):
for p in dups:
p.unlink()
return super(Followers, self).create(vals)
This error is because an SQL_constraint
('mail_followers_res_partner_res_model_id_uniq', 'unique(res_model,res_id,partner_id)', 'Error, a partner cannot follow twice the same object.'), ...
Located in /addons/mail/models/mail_followers.py, when you create an object the current user added automatically as follower twice in this models ['mail.followers'], so the sql_constraint will be : Unique('your_model', id_of_record_you_want_to_create, current_user) twice.
Solution : you can use option .with_context(mail_create_nosubscribe=True) and this means do not add automatically the current user as followers.
Exemple :
self.env['helpdesk.ticket'].with_context(mail_create_nosubscribe=True).create({
'name' : 'ticket_name',
'partner_id' : 22,
'team_id' : 2,
})
I am trying to create a function field which will get the current membership type of a member and store it in a new field in res.partner. However this code is not getting called whenever I am creating or editing membership of a member. But if I remove the store attribute the functional field works just as expected. Note that I am reusing the membership module of openerp and using odoo8 now. I am attaching the code, please let me know where am I going wrong. I need this method to be called atleast when I am using the store attribute. Am I using the store attribute incorrectly:
from openerp.osv import osv, fields
class partner_member(osv.Model):
'''Partner'''
_inherit = 'res.partner'
def _get_membership_type(self,cr,uid,ids,context=None):
member_line_obj = self.pool.get('membership.membership_line')
partner_list = []
for line in member_line_obj.browse(cr, uid, ids, context=context):
if line.state == 'paid':
partner_list.append(line.partner.id)
return partner_list
def _current_membership(self, cr, uid, ids, field_name= None, arg=False, context=None):
res_obj = self.pool.get('res.partner')
res_data_obj = res_obj.browse(cr, uid, ids, context=context)
res=dict()
for member in res_data_obj:
if member.member_lines:
for lines in member.member_lines:
if (lines.state == 'paid'):
res[member.id] = lines.membership_id.name_template
break
else:
res[member.id] = 'None'
else:
res[member.id] = 'None'
return res
_columns = {
'current_membership':
fields.function(_current_membership,type='char',
string='Current Membership Type',
store = {
'membership.membership_line':
(_get_membership_type, ['state'], 10)
},
help="Shows the current membership of a user"),
}
You made a mistake on the _get_membership_type() method. Indeed, you return only the list of res.partner that are in a line with state == 'paid'.
I think you must return all the partner that are in lines no matter the state of the line.
def _get_membership_type(self,cr,uid,ids,context=None):
member_line_obj = self.pool.get('membership.membership_line')
partner_list = []
for line in member_line_obj.browse(cr, uid, ids, context=context):
partner_list.append(line.partner.id)
return partner_list
If you want your function _current_membership to be a method of your class (as you did) you need to add the method=True parameter to your field definition:
_columns = {
'current_membership':
fields.function(_current_membership,type='char',
string='Current Membership Type',
store = {
'membership.membership_line':
(_get_membership_type, ['state'], 10)
},
help="Shows the current membership of a user",
method=True),
}
That should resolve your problem.
Certainly you can simply use store=True to have your field recalculated on every change in whatever field of your object.