Odoo Singleton error when putting more then one attachment - singleton

Hello i am trying to send multiple attachment in one email, here is the code that i used:
def action_send_card(self):
template_id = self.env.ref('library.agreement_email_template').id
data_id = self.env['ir.attachment'].browse(self.agreement_file.id)
template = self.env['mail.template'].browse(template_id)
for pdf in data_id:
template.attachment_ids = [(3, pdf.id)]
template.attachment_ids = [(6, 0, [pdf.id])]
template.send_mail(self.id, force_send=True)
return True
i tried it with one file its work great but when i try two files or more it gives me the same error.
ValueError: Expected singleton: ir.attachment(260, 257)
The agreement_file is declared like this:
agreement_file = fields.Many2many(
'ir.attachment',
'class_ir_attachments_rel',
'class_id',
'attachment_id',
string="Agreement files",
required=False)

self possible to contains multi recordset and methods required single record.
Try with following code:
def action_send_card(self):
template_id = self.env.ref('library.agreement_email_template').id
for record in self:
data_id = self.env['ir.attachment'].browse(record.agreement_file.id)
template = self.env['mail.template'].browse(template_id)
for pdf in data_id:
template.attachment_ids = [(3, pdf.id)]
template.attachment_ids = [(6, 0, [pdf.id])]
template.send_mail(record.id, force_send=True)
return True
For detailed information about this issue: https://odedrabhavesh.blogspot.com/2017/02/valueerror-expected-singleton-in-odoo.html

I tried some of the solutions in here but it still didnt work. I changed something in the code and somehow i found the solution. Here it is
def action_send_card(self):
template_id = self.env.ref('library.agreement_email_template').id
data_id = self.env['ir.attachment'].browse(self.agreement_file.ids)
template = self.env['mail.template'].browse(template_id)
for existing_pdf in template.attachment_ids:
template.write({"attachment_ids": [(3, existing_pdf.id)]})
for pdf in data_id:
for new_pdf in pdf:
template.write({"attachment_ids": [(4, new_pdf.id)]})
template.send_mail(self.id, force_send=True)

agreement_file is amny2many, for your particular condition there are multiple ir.attachment records assigned. Problem is occurring from line 3 data_id = self.env['ir.attachment'].browse(self.agreement_file.id), as agreement_file is a recordset of multiple records, you have to access ids instead of id.
def action_send_card(self):
template_id = self.env.ref('library.agreement_email_template').id
data_id = self.env['ir.attachment'].browse(self.agreement_file.ids)
template = self.env['mail.template'].browse(template_id)
for pdf in data_id:
template.attachment_ids = [(3, pdf.id)]
template.attachment_ids = [(6, 0, [pdf.id])]
template.send_mail(self.id, force_send=True)
return True
or simpler
def action_send_card(self):
template = self.env.ref('library.agreement_email_template')
template.attachment_ids = [(6, 0, self.agreement_file.ids)]
template.send_mail(self.id, force_send=True)
return True

Related

Update fields with different number of values

In res.partner model I have a field
property_product_pricelist = fields.Many2many('product.pricelist')
and in sale.order
alternative_pricelist_ids = fields.Many2many(
'product.pricelist')
Partner can have more than one price list so my goal is to add the first pricelist to the pricelist_id field and other pricelists to alternative_pricelist_ids. The things are that how I wrote code is not really good, as you can see I will get an error if there are more than 4 pricelists. So how can I avoid it and write it another way?
#api.multi
#api.onchange('partner_id')
def onchange_partner_id(self):
super(SaleOrder,self).onchange_partner_id()
values = { 'pricelist_id': self.partner_id.property_product_pricelist[0] and self.partner_id.property_product_pricelist.id[0] or False,
'alternative_pricelist_ids': self.partner_id.property_product_pricelist[1] and self.partner_id.property_product_pricelist[2] and self.partner_id.property_product_pricelist[3] or False,
}
self.update(values)
Try this :
#api.multi
#api.onchange('partner_id')
def onchange_partner_id(self):
super(SaleOrder, self).onchange_partner_()
for record in self:
pricelist_id = False
alternative_ids = []
for pricelist in record.partner_id.property_product_pricelist:
if not pricelist_id:
pricelist_id = pricelist.id
else:
alternative_ids.append(pricelist.id)
record.pricelist_id = pricelist_id
record.alternative_pricelist_ids = [(6, 0, alternative_ids)]

Scrapy items outputed as tuples instead of list

For whatever reason all items are returned as tuples. Not sure what I am missing. In all other spiders and projects it was just a list (when i use extract()).
{'acne': (None,),
'function': ([u'\u2027Preservative'],),
'function0': u'\u2027Preservative',
'irritant': (None,),
'name': (u'Potassium Sorbate',),
'safety': (u'3',),
'url': 'http://cosdna.com/eng/383bb7435.html'}
Here is my spider code.
def parse(self, response):
inspect_response(response, self)
a = response.xpath('//table//tr')
for i in a:
item = CosdnaExtItem()
item['name'] = i.xpath('./td/a/text()').extract_first(),
item['url'] = i.xpath('./td/a/#href').extract_first(),
item['function'] = i.xpath('.//td[2]/span//text()').extract(),
item['acne'] = i.xpath('.//td[3]/span//text()').extract_first(),
item['irritant'] = i.xpath('.//td[4]/span//text()').extract_first(),
item['safety'] = i.xpath('.//td[5]/div//text()').extract_first(),
yield item
Note extra commas at the end of lines:
item['function'] = i.xpath('.//td[2]/span//text()').extract(),
in Python
x = y,
is the same as
x = (y,)

Odoo 9 context value missing in override method

in odoo9 I override the search_read method. The super method works ok. With the data returned I want to make a filter, the filter is on the context, the value was asigned on the click of the button comming from the view.
<button name="status_instalacion" string="Instalación" type="action" icon="fa-wrench fa-2x" context="{'stage_id' : 1, 'current_id': active_id}"/>
The problem occurs when I query the context in the search_read method. It exists but doesn't have the values I placed
context on click of button:
self._context
{u'lang': u'en_US', u'stage_id': 1, u'tz': False, u'uid': 1, u'current_id': 40, u'tipo_validacion': u'Sistemas Cr\xedticos', u'sistema_critico': u'AGUA'}
the stage_id is the value I want
context on read_search:
self._context
{u'lang': u'en_US', u'bin_size': True, u'tipo_validacion': u'Sistemas Cr\xedticos', u'tz': False, u'uid': 1,
u'active_test': False, u'sistema_critico': u'AGUA'}
as you can see the 'stage_id' value is missing
Tried also assigning the value to a property of the class, but the value never changes it is always the initial value.
from logging import getLogger
from openerp import api, fields, models
_logger = getLogger(__name__)
class MgmtsystemSistemasEquipos(models.Model):
""" Equipos."""
_name = 'mgmtsystem.sistemas.equipos'
dmy = 99 # ---> this value never changes
def dummy(self): # ---> tried calling a function. not work
return self.dmy
def set_dummy(self, id): # ----> set the value
self.dmy = id or self.dmy
codigo = fields.Char(
string=u'Código',
help=u"Código equipo",
required=True,
size=30)
name = fields.Char(
string=u'Nombre equipo',
required=True,
readonly=False,
index=True,
help="Nombre corto equipo",
size=30)
stage_id = fields.Many2one(
'mgmtsystem.action.stage',
'Fase',
default=_default_stage,
readonly=True)
#api.multi
def status_instalacion(self):
import pudb
pu.db
# save value to variable dmy to retrieve later
id = self._context.get('stage_id')
self.set_dummy(id)
#api.model
def search_read(
self, domain=None, fields=None, offset=0,
limit=None, order=None):
import pudb
pu.db
# here the variable allways has the original value (99)
current_stage_id = self.dmy
current_stage_id = self.dummy()
current_stage_id = getattr(self, dmy)
res = super(MgmtsystemSistemasEquipos, self).search_read(
domain, fields, offset, limit, order)
current_id = res[0]['id']
valid_protocols_ids = self._get_ids(
current_stage_id, current_id,
'mgmtsystem_equipos_protocolos',
'mgmtsystem_equipos_protocolos_rel',
'protocolo_id')
# # remove ids
res[0]['protocolos_ids'] = valid_protocols_ids
res[0]['informes_ids'] = valid_informes_ids
res[0]['anexos_ids'] = valid_anexos_ids
return res
# #api.multi
def _get_ids(self, current_stage_id, current_id, model, model_rel, field_rel):
import pudb
pu.db
# in this method the value of the variable is allways the original
current_stage_id = self.dummy()
sql = """ select a.id from
%s as a
join %s as b
on a.id = b.%s where b.equipo_id = %s
and a.stage_id = %s; """ % (model, model_rel, field_rel,
current_id, current_stage_id)
import psycopg2
try:
self.env.cr.execute(sql)
except psycopg2.ProgrammingError, ex:
message = 'Error trying to download data from server. \n {0} \n {1}'.format(ex.pgerror, sql)
_logger.info(message)
return False
rows = self.env.cr.fetchall()
list_of_ids = []
for row in rows:
list_of_ids.append(row[0])
return list_of_ids
I don't know Python very well, and thats the cause of my misunderstanding of how to read the value of the variable.
But then again, Why is the context modified in the search_read method?.
Thank you.
You should try following.
#api.model
def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
import pudb
pu.db
# Here you need to get the value from the context.
current_stage_id = self._context.get('stage_id', getattr(self, dmy))
res = super(MgmtsystemSistemasEquipos, self).search_read(domain=domain, fields=fields, offset=offset, limit=limit, order=order)
current_id = res[0]['id']
valid_protocols_ids = self._get_ids(
current_stage_id, current_id,
'mgmtsystem_equipos_protocolos',
'mgmtsystem_equipos_protocolos_rel',
'protocolo_id')
# # remove ids
res[0]['protocolos_ids'] = valid_protocols_ids
res[0]['informes_ids'] = valid_informes_ids
res[0]['anexos_ids'] = valid_anexos_ids
return res
In your code those lines won't work just because there is no recordset available in self (it's correct behaviour search_read must have #api.model decorator).
# here the variable allways has the original value (99)
current_stage_id = self.dmy
current_stage_id = self.dummy()
current_stage_id = getattr(self, dmy)
So just remove those and lines and apply some other logic to get data.

How to override create methode in odoo 10

i want to use same create method in odoo 10 as below means i want to convert below code in odoo 10, below code is working well for odoo 8
def create(self, cr, uid, vals, context=None):
phase_obj = self.pool.get('hr_evaluation.plan.phase')
survey_id = phase_obj.read(cr, uid, vals.get('phase_id'), fields=['survey_id'], context=context)['survey_id'][0]
if vals.get('user_id'):
user_obj = self.pool.get('res.users')
partner_id = user_obj.read(cr, uid, vals.get('user_id'), fields=['partner_id'], context=context)['partner_id'][0]
else:
partner_id = None
user_input_obj = self.pool.get('survey.user_input')
if not vals.get('deadline'):
vals['deadline'] = (datetime.now() + timedelta(days=28)).strftime(DF)
ret = user_input_obj.create(cr, uid, {'survey_id': survey_id,
'deadline': vals.get('deadline'),
'type': 'link',
'partner_id': partner_id}, context=context)
vals['request_id'] = ret
return super(hr_evaluation_interview, self).create(cr, uid, vals, context=context)
i am trying below code:
def create(self, vals):
survey_id = self.env['hr_evaluation.plan.phase'].read(vals.get('phase_id'),fields=['survey_id'])['survey_id'][0]
if vals.get('user_id'):
partner_id = self.env['res.users'].read(vals.get('user_id'), fields=['partner_id'])['partner_id'][0]
else:
partner_id = None
if not vals.get('deadline'):
vals['deadline'] = (datetime.now() + timedelta(days=28)).strftime(DF)
ret = self.env['survey.user_input'].create({'survey_id': survey_id,
'deadline': vals.get('deadline'),
'type': 'link',
'partner_id': partner_id})
vals['request_id'] = ret
return super(hr_evaluation_interview, self).create(vals)
but it is giving me error like TypeError: read() got multiple values for keyword argument 'fields' so please guide me how can i remove this error?
read method accept fields as argument and you give it two arguments.
read([fields])
Reads the requested fields for the records in self, low-level/RPC method. In Python code, prefer browse().
Parameters
fields -- list of field names to return (default is all fields)
Returns
a list of dictionaries mapping field names to their values, with one dictionary per record
Raises
AccessError -- if user has no read rights on some of the given records
Instead of calling read method it's better to call browse() method, you can read Browse() vs read() performance in Odoo 8
Your code should be:
def create(self, vals):
survey_id = self.env['hr_evaluation.plan.phase'].browse(vals.get('phase_id'))
if vals.get('user_id'):
partner_id = self.env['res.users'].browse(vals.get('user_id'))
else:
partner_id = None
if not vals.get('deadline'):
vals['deadline'] = (datetime.now() + timedelta(days=28)).strftime(DF)
ret = self.env['survey.user_input'].create({'survey_id': survey_id.id,
'deadline': vals.get('deadline'),
'type': 'link',
'partner_id': partner_id.id})
vals['request_id'] = ret.id
return super(hr_evaluation_interview, self).create(vals)

Auto fill on2many field on form load odoo 8

I have tried to create a functional field with type="one2many" and auto fill on form load. I tried below code:
Code 1:
'flat_members1': fields.function(_get_flat_members, relation="family.info", method=True, type="one2many", multi='flat_fkk'),
def _get_flat_members(self, cr, uid, ids, name, arg, context=None):
cr.execute("Select * from family_info where flat="+str(flat_id)+"")
cr_res = cr.dictfetchall()
res = {}
for data in self.browse(cr,uid,ids):
res[data.id] = self.pool.get('family.info').search(cr,uid,[('flat', '=', flat_id)])
return values
Code 2:
member_ids = []
for res in cr_res:
member_ids.append((0,0,{'name':res.get('name'),
'flat':res.get('flat'),
}))
values.update(family_members1=member_ids)
return values
In both way i got an error:
AttributeError: 'list' object has no attribute 'iteritems'
Please suggest me a solution thanks.
Use Odoo8 new api:
flat_members1 = fields.One2many(compute='_get_flat_members',
comodel_name='family.info',
string='flat_members1',
store=True)
#api.one
#api.depends('flat_id')
def _get_flat_members(self):
member_ids = []
# get member_ids
self.flat_members1 = member_ids