I have been trying with sending mails whenever there is a leave request/approval/rejection in Leave request and I have been following the steps in the following link - https://www.odoo.com/forum/help-1/question/openerp-7-sending-email-to-manager-when-employee-applies-a-leave-7362
By following the above steps, messages do appear in Odoo Inbox but I am not able to get the mail delivered to mail client like thunderbird
Anyone kindly can give an opinion ?
Here is the code that you want,
Code for leave request:
def create(self, cr, uid, values, context=None):
res = super(hr_holidays, self).create(cr, uid, values, context=context)
template = self.pool.get('ir.model.data').get_object(cr, uid, 'hr_holidays_aspire', 'leave_request')
mail_id = self.pool.get('email.template').send_mail(cr, uid, template.id, res , force_send=True)
return res
Code for leave approve:
def holidays_validate(self, cr, uid, ids, context=None):
compose_ctx = dict(context,active_ids=ids)
search_domain = [('name', '=', 'Leave Approved')]
template_id = self.pool['email.template'].search(cr, uid, search_domain, context=context)[0]
compose_id = self.pool['mail.compose.message'].create(
cr, uid, {
'model': self._name,
'composition_mode': 'mass_mail',
'template_id': template_id,
'post': True,
'notify': True,
}, context=compose_ctx)
self.pool['mail.compose.message'].write(
cr, uid, [compose_id],
self.pool['mail.compose.message'].onchange_template_id(
cr, uid, [compose_id],
template_id, 'mass_mail', self._name, False,
context=compose_ctx)['value'],
context=compose_ctx)
self.pool['mail.compose.message'].send_mail(cr, uid, [compose_id], context=compose_ctx)
return True
Code for leave refuse:
def holidays_refuse(self, cr, uid, ids, context=None):
compose_ctx = dict(context,active_ids=ids)
search_domain = [('name', '=', 'Leave Refused')]
template_id = self.pool['email.template'].search(cr, uid, search_domain, context=context)[0]
compose_id = self.pool['mail.compose.message'].create(
cr, uid, {
'model': self._name,
'composition_mode': 'mass_mail',
'template_id': template_id,
'post': True,
'notify': True,
}, context=compose_ctx)
self.pool['mail.compose.message'].write(
cr, uid, [compose_id],
self.pool['mail.compose.message'].onchange_template_id(
cr, uid, [compose_id],
template_id, 'mass_mail', self._name, False,
context=compose_ctx)['value'],
context=compose_ctx)
self.pool['mail.compose.message'].send_mail(cr, uid, [compose_id], context=compose_ctx)
return super(hr_holidays, self).holidays_refuse(cr, uid, ids, context=context)
Here is the three different template for Leave request,approve and refuse respectively.
<!-- Templates for requesting leave -->
<record id="leave_request" model="email.template">
<field name="name">Leave Request</field>
<field name="subject">Leave Application from ${object.employee_id.name} [${object.employee_id.employee_no}].</field>
<field name="email_from">${object.employee_id.work_email}</field>
<field name="email_to">${object.employee_id.parent_id.work_email}</field>
<field name="model_id" ref="model_hr_holidays"/>
<field name="user_signature" eval="0"/>
<field name="body_html"><![CDATA[<p>Hi,</p>
<p> ${object.employee_id.name} [${object.employee_id.employee_no}] has applied for a leave application.</p><br/>
<p>Following are the applied leave details: </p>
<p>Leave type: ${object.holiday_status_id.name}</p>
<p>From Date: ${object.date_from}</p>
<p>To Date: ${object.date_to}</p>
<p>Number of days: ${object.number_of_days_temp}</p>
<p>Reason: ${object.name}</p>
<p>From Session: ${object.from_session}</p>
<p>To Session: ${object.to_session}</p>
<br/><br/>
<p>Regards, ${object.employee_id.name}</p>
]]></field>
</record>
<!-- Templates for approving leave -->
<record id="leave_approve" model="email.template">
<field name="name">Leave Approved</field>
<field name="subject">Your Leave Application has been Accepted.</field>
<field name="email_from">${object.employee_id.parent_id.work_email}</field>
<field name="email_to">${object.employee_id.work_email}</field>
<field name="model_id" ref="model_hr_holidays"/>
<field name="user_signature" eval="0"/>
<field name="body_html"><![CDATA[<p>Hi ${object.employee_id.name} [${object.employee_id.employee_no}], </p>
<p>Your leave application has been accepted.</p><br/>
<p>Leave type: ${object.holiday_status_id.name}</p>
<p>From Date: ${object.date_from}</p>
<p>To Date: ${object.date_to}</p>
<p>Number of days: ${object.number_of_days_temp}</p>
<p>Reason: ${object.name}</p>
<p>From Session: ${object.from_session}</p>
<p>To Session: ${object.to_session}</p>
<br/><br/>
<p>Regards, ${object.employee_id.parent_id.name}</p>
]]></field>
</record>
<!-- Templates for refusing leave -->
<record id="leave_refuse" model="email.template">
<field name="name">Leave Refused</field>
<field name="subject">Your Leave Application has been Refused.</field>
<field name="email_from">${object.employee_id.parent_id.work_email}</field>
<field name="email_to">${object.employee_id.work_email}</field>
<field name="model_id" ref="model_hr_holidays"/>
<field name="user_signature" eval="0"/>
<field name="body_html"><![CDATA[<p>Hi ${object.employee_id.name} [${object.employee_id.employee_no}], </p>
<p>Your leave application has been refused.</p><br/>
<p>Leave type: ${object.holiday_status_id.name}</p>
<p>From Date: ${object.date_from}</p>
<p>To Date: ${object.date_to}</p>
<p>Number of days: ${object.number_of_days_temp}</p>
<p>Reason: ${object.name}</p>
<p>From Session: ${object.from_session}</p>
<p>To Session: ${object.to_session}</p>
<br/><br/>
<p>Regards, ${object.employee_id.parent_id.name}</p>
]]></field>
</record>
Cheers !!!
Related
i have override product_id_change() of sale.order.line its working fine.
Now my requirement is to get sale.order fields in my onchange so how can i get field value of sale.order from sale.order.line product_id_change()
here is my code odoov8
def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
#how to access self.order_id
print "order id===", order_id
res = super(sale_order_line, self).product_id_change( cr, uid, ids, pricelist, product, qty=qty,uom=uom, qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id,lang=lang, update_tax=update_tax, date_order= date_order, packaging=packaging, fiscal_position=fiscal_position, flag=flag, context=context)
return res
You can do it using following steps.
1.In On change method you can pass one argument parent.
<record model="ir.ui.view" id="sale_margin_sale_order_line">
<field name="name">sale.order.line.margin_and_quantities.view.form</field>
<field name="type">form</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[#name='order_line']/form[#string='Sales Order Lines']/group/group[1]/field[#name='product_id']"
position="attributes"> position="attributes">
<attribute name="on_change">
product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,
product_uos,name,parent.partner_id, False, False, parent.date_order, False, parent.fiscal_position, True, context,parent)
</attribute>
</xpath>
</field>
</record>
In above view we have used position attributes option to replace on_change method.
In on_change method just add one parameter at last parent, parent means sale.order object.
product_id field is available in sale.order.line, you can access order_id using parent keyword in the view.
2.Inherit product_id_change method in py file.
def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
uom=False, qty_uos=0, uos=False, name='', partner_id=False,
lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None,order_id=False):
res=super(sale_order,self).product_id_change(cr,uid,ids,pricelist,product,qty,uom,qty_uos,uos,name,partner_id,lang,update_tax,date_order,packaging,fiscal_position,flag,context)
return res
In above method order_id is available in method argument, so you
can directly access it.
If you have installed sale_stock module then you should inherit
product_id_change_with_wh method in py file & change position attributes in product_id_change_with_wh on_change in view, also
must give dependency of sale_stock in openerp.py file.
After that you will get order_id field in
product_id_change_with_wh on_change method & pass this parameter in on_change product_id method.
Ex :
def product_id_change_with_wh(self, cr, uid, ids, pricelist, product, qty=0,
uom=False, qty_uos=0, uos=False, name='', partner_id=False,
lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, warehouse_id=False, context=None,order_id=False):
context = context or {}
product_uom_obj = self.pool.get('product.uom')
product_obj = self.pool.get('product.product')
warning = {}
#UoM False due to hack which makes sure uom changes price, ... in product_id_change
res = self.product_id_change(cr, uid, ids, pricelist, product, qty=qty,
uom=False, qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id,
lang=lang, update_tax=update_tax, date_order=date_order, packaging=packaging, fiscal_position=fiscal_position, flag=flag, context=context,order_id=order_id)
This method is written in the old API so you have to browse the sale.order.line record, which is changed.
def product_id_change(
self, cr, uid, ids, pricelist, product, qty=0, uom=False,
qty_uos=0, uos=False, name='', partner_id=False, lang=False,
update_tax=True, date_order=False, packaging=False,
fiscal_position=False, flag=False, context=None):
line = self.browse(cr, uid, ids[0], context)
# print line.order_id.name
res = super(sale_order_line, self).product_id_change(
cr, uid, ids, pricelist, product, qty=qty,uom=uom,
qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id
lang=lang, update_tax=update_tax, date_order= date_order,
packaging=packaging, fiscal_position=fiscal_position,
flag=flag, context=context)
return res
I Created a new module and inherited stock.picking module.In that added Accepted state between ready-to transfer and transfer state in warehouse. and a accept Button in header.
while clicking on accept button ,function is executing but state is not changing to accepted state.other functions of existing module are working correctly.
I added new accept selection field in existing module. i.e stock.picking
.py file
from openerp.osv import fields, osv
from openerp.exceptions import except_orm, Warning, ValidationError
import logging
class inventory_button_action(osv.osv):
_inherit = 'stock.picking'
def execute_accept_button(self, cr, uid, ids, context=None):
log = logging.getLogger(__name__)
log.info('######### Executed 11111 ########')
self.change_state_accept(cr, uid, ids, context=context)
def change_state_accept(self, cr, uid, ids, context=None):
log = logging.getLogger(__name__)
obj = self.pool.get('stock.picking')
obj.write(cr, uid, ids, {'state': 'accept'},context=context)
log.info('######### Executed 2222 ########')
xml file
<?xml version="1.0"?>
<openerp>
<data>
<record id="inventory_form_view" model="ir.ui.view">
<field name="name">inventory_status_form</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//form/header/button[#name='do_enter_transfer_details']" position="before">
<button string="Accept" name="execute_accept_button" type="object" attrs="{'invisible':['|', ('state', 'in', ('draft','cancel','waiting','confirmed','partially_available','accept','done'))]}"/>
</xpath>
</field>
</record>
</data>
</openerp>
in stock.picking module
_columns = {
'name': fields.char('Reference', select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, copy=False),
'origin': fields.char('Source Document', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="Reference of the document", select=True),
'backorder_id': fields.many2one('stock.picking', 'Back Order of', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="If this shipment was split, then this field links to the shipment which contains the already processed part.", select=True, copy=False),
'note': fields.text('Notes'),
'move_type': fields.selection([('direct', 'Partial'), ('one', 'All at once')], 'Delivery Method', required=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="It specifies goods to be deliver partially or all at once"),
'state': fields.function(_state_get, type="selection", copy=False,
store={
'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_type'], 20),
'stock.move': (_get_pickings, ['state', 'picking_id', 'partially_available'], 20)},
selection=[
('draft', 'Draft'),
('cancel', 'Cancelled'),
('waiting', 'Waiting Another Operation'),
('confirmed', 'Waiting Availability'),
('partially_available', 'Partially Available'),
('assigned', 'Ready to Transfer'),('accept','Accepted'),
('done', 'Transferred'),
], string='Status', readonly=True, select=True, track_visibility='onchange')}
You do not need a browse record stock.picking to save a value.
When you write stok_obj.state = 'accept' you have just change instance value nothing will be saved in the database (available since odoo-8).
To change state to accept you can use write function:
openerp:
def execute_accept_button(self, cr, uid, ids, *args):
self.write(cr, uid, ids, {'state': 'accept'})
return True
Odoo:
#api.multi
def execute_accept_button(self):
self.write({'state': 'accept'})
return True
Try following:
def chnage_state_accept(self, cr, uid, ids, context=None):
self.write(cr,uid,ids,{'state':'accept'},context=context)
return <<return value if needed>>
I have a wizard below that writes to the database but doesnt read the values written to the database and display in the view.
I using the get_opinion to read the opinions and the get_notes to read the notes , but when i click on the buttons they just disappear with no error , where am i going wrong
Q2 , in this code which is part of the code below why am i getting model_name none
if context is None: context = {}
model_name=context.get('active_model')
print model_name #gives NONE why ?
Q3 How can i list all fields in context?
The module code is as below
class opinion(models.TransientModel):
_name='opinion'
opinion_emission = fields.Text(string='OPINION EMISSION')
notes = fields.Text(string='Additional Notes')
defaults={
'opinion_emission': lambda self : self.get_opinion(self),
'notes': lambda self : self.get_notes(self),
}
def save_it(self, cr, uid,ids , context=None):
# context = dict(self._context or {} )
if context is None: context = {}
active_id = context.get('active_id',False)
print 'active_id' , active_id
if active_id :
# op = self.env['opinion'].browse(active_id)
info = self.browse(cr,uid,ids)
self.pool.get('opinion').write(cr,uid,context['active_id'],{'opinion_emission': info[0].opinion_emission,'notes': info[0].notes})
return {
'type': 'ir.actions.act_window_close',
}
#functions that get the info stored in db
#api.one
def get_opinion(self):
ids=self._ids
cr = self._cr
uid = self._uid
context = self._context
if context is None: context = {}
active_id = context.get('active_id',False)
print 'active_id' , active_id
if active_id :
return self.env['opinion'].browse(cr, uid, context['active_id'], context).opinion_emission
#api.one
def get_notes(self,records=None):
ids=self._ids
cr = self._cr
uid = self._uid
context = self._context
if context is None: context = {}
model_name=context.get('active_model')
print model_name #gives NONE why ?
active_id = context.get('active_id',False)
print 'active_id' , active_id
if active_id :
print 'output',self.env['opinion'].browse(cr, uid, context['active_id'], context)[0].notes
return self.env['opinion'].browse(cr, uid, context['active_id'], context)[0].notes
The xml view code is :
<openerp>
<data>
<record id="view_opinion_wizard" model="ir.ui.view">
<field name="name">opinion_wizard.form</field>
<field name="model">opinion</field>
<field name="type">form</field>
<field name="arch" type="xml">
<!-- create a normal form view, with the fields you've created on your python file -->
<form string="OPINION" version="8.0">
<button icon="gtk-ok" name="get_notes" string='SET VAL' type="object" />
<group >
<separator string="Please insert OPINION" colspan="2"/>
<field name="opinion_emission" string="OPINION EMISSION "/>
<field name="notes" string="NOTES"/>
<newline/>
</group>
<div style="text-align:right">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-ok" name="save_it" string="SAVE INFO" type="object" />
<button icon="gtk-ok" name="get_notes" string="GET NOTES" type="object" />
<button icon="gtk-ok" name="get_opinion" string="GET OPINION" type="object" />
</div>
</form>
</field>
</record>
<!-- your action window refers to the view_id you've just created -->
<record id="action_opinion" model="ir.actions.act_window">
<field name="name">OPINION</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">opinion</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_opinion_wizard"/>
<field name="target">new</field>
</record>
<menuitem name="OPINION" id="menu_opinion" action="action_opinion" parent="timeorder_ratecard_rnd_menu"/>
</data>
This is new API code try this code:
class opinion(models.TransientModel):
_name = 'opinion'
opinion_emission = fields.Text(default=lambda self: self.get_opinion(self), string='OPINION EMISSION')
notes = fields.Text(default=lambda self: self.get_notes(self), string='Additional Notes')
#api.multi
def save_it(self):
active_id = self.env.context.get('active_id', False)
print 'active_id', active_id
if active_id:
op = self.env['opinion'].browse(active_id)
op.write({'opinion_emission': self.opinion_emission, 'notes': self.notes})
return {'type': 'ir.actions.act_window_close', }
#functions that get the info stored in db
#api.one
def get_opinion(self):
active_id = self.env.context.get('active_id', False)
print 'active_id', active_id
if active_id:
return self.env['opinion'].browse(active_id).opinion_emission
#api.one
def get_notes(self, records=None):
model_name = self.env.context.get('active_model')
print model_name # gives NONE why ?
active_id = self.env.context.get('active_id', False)
print 'active_id', active_id
if active_id:
print 'output', self.env['opinion'].browse(active_id).notes
return self.env['opinion'].browse(active_id).notes
Please me to solve this problems. I'm using openerp to download attendance machine and insert that data into my table but i have to view the data from the machine into grid using using one2many field how could I passing the data after I downloaded this my script. I'm using zkclock module to get the data.
hr_machinezem560.py
....
import zkclock
import from osv import osv, fields
....
class hr_machinezem560(osv.osv):
_name = "hr_machinezem560"
_columns = {
'date_from':fields.date("Date From"),
'date_to':fields.date("Date To"),
'proses_id':fields.one2many('hr.machinezem560_details', 'proses_id', 'Prosess Download')
}
def action_download(self, cr, uid, ids, context=None):
domain = {}
params = self.browse(cr, uid, ids, context=context)[0]
start_date = date_object = datetime.datetime.strptime(params.date_from, '%Y-%m-%d')
end_date = date_object = datetime.datetime.strptime(params.date_to, '%Y-%m-%d')
date_list = [dt.strftime("%Y-%m-%d") for dt in rrule(DAILY, dtstart=start_date, until=end_date)]
host = '192.168.1.201'
port = 4370
password = ''
s, r = zkclock.connect(host, port, password)
r = zkclock.disable(s, r)
r, userdata = zkclock.get_user_data(s, r)
r, logdata = zkclock.get_log_data(s, r)
r = zkclock.enable(s, r)
r = zkclock.disconnect(s, r)
if userdata and logdata:
logdata = zkclock.assemble_log_data_from_packets(logdata)
users = zkclock.unpack_users_to_dict(userdata)
logging.debug(users)
loglist = []
while((len(logdata)/LOG_DATA_SIZE) >=1):
loglist.append(logdata[:LOG_DATA_SIZE])
logdata = logdata[LOG_DATA_SIZE:]
logs = []
for i in loglist:
log_entry = zkclock.unpack_log(i)
timestamp = zkclock.decode_time(log_entry.time)
verification_method = log_entry.decode_verified()
try:
user_name = users[log_entry.uid].name
except KeyError:
user_name = "Unknown user: %s" % log_entry.uid
logs.append([timestamp,str(log_entry.uid), verification_method])
attance_det = self.pool.get("hr.machinezem560_details")
for log in logs:
if log[0].strftime('%Y-%m-%d') in date_list:
values = {
'employee': log[1],
'timesheet': log[0],
'status':log[2]
}
attance_det.create(cr, uid, values, context=context)
hr_machinezem560()
class hr_machinezem560_details(osv.osv):
_name = "hr.machinezem560_details"
_columns = {
'employee': fields.char("Employee Name", size=80),
'timesheet':fields.datetime("Time Sheet"),
'status': fields.char("Status", size=10),
'proses_id':fields.many2one('hr_machinezem560','Prosess Download')
}
hr_machinezem560_details()
view:
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="hr_batch_machine_view_form" model="ir.ui.view">
<field name="name">Attendance Machine Batch Process</field>
<field name="model">hr_machinezem560</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Download ZEM560 Data" version="7.0">
<group name="up">
<label for="date_from" string="Timesheet Period"/>
<div>
<field name="date_from" class="oe_inline"/> to <field name="date_to" class="oe_inline"/>
<div class="oe_right oe_button_box" name="right_top_button">
<button name="action_download" string="Download" type="object"/>
</div>
</div>
</group>
<group name="down">
<field name="proses_id" colspan="3" nolabel="1" readonly="1">
<tree string="Attendance Data">
<field name="employee"/>
<field name="timesheet"/>
<field name="status"/>
</tree>
</field>
</group>
</form>
</field>
</record>
<record id="action_batch_hr_machine_view" model="ir.actions.act_window">
<field name="name">Attendance Machine Batch Proses</field>
<field name="res_model">hr_machinezem560</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<menuitem
id="menu_hr_absensiRoot"
name="Absensi"
sequence="3"
parent="hr.menu_hr_root"
/>
<menuitem
id="menu_hr_mesin_Absensi"
name="Mesin Absensi ZEM560"
sequence="1"
parent="menu_hr_absensiRoot"
action="action_batch_hr_machine_view"
/>
</data>
</openerp>
I am trying to achieve that all the users that are Hr managers should be allowed to see the records.
I have created a Functional Field:
def list_HRM(self, cr, uid, ids, field_name, arg, context):
attribute = {}
hr_managers = self.pool.get('hr.employee').search(cr, uid, ['&', ('department_id.name', '=', 'Human Resources'), ('manager', '=', True)], context=context)
hr_managers_uid = []
for record in hr_managers:
hr_managers_uid.append(self.pool.get('hr.employee').browse(cr, uid, record, context=context).user_id.id)
record = self.browse(cr, uid, ids)[0]
attribute[record.id] = str(uid in hr_managers_uid or uid==1)
return attribute
_columns={
'hr_managers_func' : fields.function(list_HRM, type='char', method=True, string='List of HR Managers'),
'always_true':fields.boolean()
}
_defaults={
'always_true':True
}
In .xml file:
<field name="always_true" invisible="1"/>
<field name="hr_managers_func" invisible="1"/>
In Record Rule:
['&','|',('state','=','hod_depart'),('state','=','hr_review'),('always_true','=',eval(hr_managers_func))]
I used field 'always_true' because of the record rule condition format i.e.
[('field_name','operator',values)].
I thought that rule will evaluate the functional field using eval
but unfortunately eval is not working on the record rule ,
I am getting this error:
NameError: name 'eval' is not defined
I could not think of more than this.
I saw few forum somewhat similar to my problem, they were using the related field to avoid the functional field in the record, but here I have to check whether the current user belong to hr managers or not .
I have tried explaining this in the best possible way, Looking forward for some reply.
To restrict on a function field, you need to define a fnct_search. The functional field per se becomes a dummy.
In your model:
def _my_functional_field_search(self, cr, uid, obj, name, args, context=None):
list_of_ids = [...]
return [('id', 'in', list_of_ids)]
_columns = {
'my_functional_field': fields.function(
lambda **x: True,
fnct_search=_my_functional_field_search,
type='boolean',
method=True,
),
}
And then in your security XML file:
<record id="your_rule_id" model="ir.rule">
<field name="name">Your Rule Name</field>
<field name="model_id" ref="model_the_model" />
<field name="groups" eval="[(4, ref('group_affected_group'))]" />
<field name="domain_force">[('my_functional_field', '=', True)]</field>
</record>