+ Select a configuration package to setup automatically your + taxes and chart of accounts. +
%s' % body, + 'email_to': dest, + 'email_from': data_user.email or tools.config.options['email_from']} + self.pool.get('mail.mail').create(cr, uid, vals, context=context) msg_sent += partner.name + '\n' except Exception, e: raise osv.except_osv('Error !', e ) diff --git a/addons/account_payment/account_payment_view.xml b/addons/account_payment/account_payment_view.xml index e3e9b2ae558..4d69c2d7315 100644 --- a/addons/account_payment/account_payment_view.xml +++ b/addons/account_payment/account_payment_view.xml @@ -30,8 +30,8 @@
%s' % body, + 'res_id': obj.id, + 'model': obj._table_name, + 'auto_delete': True + }, context=context) def do_check(self, cr, uid, action, obj, context=None): @@ -438,11 +432,8 @@ the rule to mark CC(mail to any other person defined in actions)."), if len(emails) and action.act_mail_body: emails = list(set(emails)) email_from = safe_eval(action.act_email_from, {}, locals_for_emails) - - def to_email(text): - return re.findall(r'([^ ,<@]+@[^> ,]+)', text or '') - emails = to_email(','.join(filter(None, emails))) - email_froms = to_email(email_from) + emails = tools.email_split(','.join(filter(None, emails))) + email_froms = tools.email_split(email_from) if email_froms: self.email_send(cr, uid, obj, emails, action.act_mail_body, emailfrom=email_froms[0]) return True diff --git a/addons/base_action_rule/i18n/nb.po b/addons/base_action_rule/i18n/nb.po new file mode 100644 index 00000000000..7aae94c417f --- /dev/null +++ b/addons/base_action_rule/i18n/nb.po @@ -0,0 +1,536 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR
Event: %s Event Date: %s Description: %s @@ -888,20 +854,21 @@ From: ---- %s - +""" % (alarm.name, alarm.trigger_date, alarm.description, \ alarm.user_id.name, alarm.user_id.signature) mail_to = [alarm.user_id.email] for att in alarm.attendee_ids: mail_to.append(att.user_id.email) if mail_to: - mail_message.schedule_with_attach(cr, uid, - tools.config.get('email_from', False), - mail_to, - sub, - body, - context=context - ) + vals = { + 'state': 'outgoing', + 'subject': sub, + 'body_html': body, + 'email_to': mail_to, + 'email_from': tools.config.get('email_from', mail_to), + } + self.pool.get('mail.mail').create(cr, uid, vals, context=context) if next_trigger_date: update_vals.update({'trigger_date': next_trigger_date}) else: @@ -1616,36 +1583,6 @@ class calendar_todo(osv.osv): calendar_todo() -class ir_attachment(osv.osv): - _name = 'ir.attachment' - _inherit = 'ir.attachment' - - def search_count(self, cr, user, args, context=None): - new_args = [] - for domain_item in args: - if isinstance(domain_item, (list, tuple)) and len(domain_item) == 3 and domain_item[0] == 'res_id': - new_args.append((domain_item[0], domain_item[1], base_calendar_id2real_id(domain_item[2]))) - else: - new_args.append(domain_item) - return super(ir_attachment, self).search_count(cr, user, new_args, context) - - def create(self, cr, uid, vals, context=None): - if context: - id = context.get('default_res_id', False) - context.update({'default_res_id' : base_calendar_id2real_id(id)}) - return super(ir_attachment, self).create(cr, uid, vals, context=context) - - def search(self, cr, uid, args, offset=0, limit=None, order=None, - context=None, count=False): - new_args = [] - for domain_item in args: - if isinstance(domain_item, (list, tuple)) and len(domain_item) == 3 and domain_item[0] == 'res_id': - new_args.append((domain_item[0], domain_item[1], base_calendar_id2real_id(domain_item[2]))) - else: - new_args.append(domain_item) - return super(ir_attachment, self).search(cr, uid, new_args, offset=offset, - limit=limit, order=order, context=context, count=False) -ir_attachment() class ir_values(osv.osv): _inherit = 'ir.values' diff --git a/addons/base_calendar/crm_meeting.py b/addons/base_calendar/crm_meeting.py index cb96ff2a4d0..e1c66148ae7 100644 --- a/addons/base_calendar/crm_meeting.py +++ b/addons/base_calendar/crm_meeting.py @@ -43,7 +43,7 @@ class crm_meeting(base_state, osv.Model): _name = 'crm.meeting' _description = "Meeting" _order = "id desc" - _inherit = ["calendar.event", 'ir.needaction_mixin', "mail.thread"] + _inherit = ["calendar.event", "mail.thread", 'ir.needaction_mixin'] _columns = { # base_state required fields 'create_date': fields.datetime('Creation Date', readonly=True), @@ -70,13 +70,17 @@ class crm_meeting(base_state, osv.Model): # OpenChatter # ---------------------------------------- + # shows events of the day for this user + def needaction_domain_get(self, cr, uid, domain=[], context={}): + return [('date','<=',time.strftime('%Y-%M-%D 23:59:59')), ('date_deadline','>=', time.strftime('%Y-%M-%D 00:00:00')), ('user_id','=',uid)] + def case_get_note_msg_prefix(self, cr, uid, id, context=None): return 'Meeting' def case_open_send_note(self, cr, uid, ids, context=None): - return self.message_append_note(cr, uid, ids, body=_("Meeting has been confirmed."), context=context) + return self.message_post(cr, uid, ids, body=_("Meeting confirmed."), context=context) def case_close_send_note(self, cr, uid, ids, context=None): - return self.message_append_note(cr, uid, ids, body=_("Meeting has been done."), context=context) + return self.message_post(cr, uid, ids, body=_("Meeting completed."), context=context) diff --git a/addons/base_calendar/crm_meeting_view.xml b/addons/base_calendar/crm_meeting_view.xml index 6009251a460..928f8d94d5c 100644 --- a/addons/base_calendar/crm_meeting_view.xml +++ b/addons/base_calendar/crm_meeting_view.xml @@ -170,7 +170,8 @@
|
+
-
+
+
-
%s. Click to sign out."), last_text, duration); + } else { + return _.str.sprintf(_t("Click to Sign In at %s."), current_text); + } + }, + html: true, + }); + return this.check_attendance(); + }, + do_update_attendance: function () { + var self = this; + var hr_employee = new instance.web.DataSet(self, 'hr.employee'); + hr_employee.call('attendance_action_change', [ + [self.employee.id] + ]).done(function (result) { + self.last_sign = new Date(); + self.set({"signed_in": ! self.get("signed_in")}); + }); + }, + check_attendance: function () { + var self = this; + self.employee = false; + this.$el.hide(); + var employee = new instance.web.DataSetSearch(self, 'hr.employee', self.session.user_context, [ + ['user_id', '=', self.session.uid] + ]); + return employee.read_slice(['id', 'name', 'state', 'last_sign']).pipe(function (res) { + if (_.isEmpty(res)) + return; + self.$el.show(); + self.employee = res[0]; + self.last_sign = instance.web.str_to_datetime(self.employee.last_sign); + self.set({"signed_in": self.employee.state !== "absent"}); + }); + }, + }); + + instance.web.UserMenu.include({ + do_update: function () { + this._super(); + var self = this; + this.update_promise = this.update_promise.then(function () { + if (self.attendanceslider) + return; + self.attendanceslider = new instance.hr_attendance.AttendanceSlider(self); + + self.attendanceslider.prependTo(instance.webclient.$('.oe_systray')); + }); + }, + }); +} diff --git a/addons/hr_attendance/static/src/xml/attendance.xml b/addons/hr_attendance/static/src/xml/attendance.xml new file mode 100644 index 00000000000..3fd9bcf2798 --- /dev/null +++ b/addons/hr_attendance/static/src/xml/attendance.xml @@ -0,0 +1,13 @@ + +
+
+
+
+ %s' % body, + 'email_to': child.work_email, + 'email_from': evaluation.employee_id.work_email} + self.pool.get('mail.mail').create(cr, uid, vals, context=context) self.write(cr, uid, ids, {'state':'wait'}, context=context) return True diff --git a/addons/hr_evaluation/hr_evaluation_data.xml b/addons/hr_evaluation/hr_evaluation_data.xml index cf632a795cc..2cf656a72bb 100644 --- a/addons/hr_evaluation/hr_evaluation_data.xml +++ b/addons/hr_evaluation/hr_evaluation_data.xml @@ -14,12 +14,14 @@
diff --git a/addons/hr_holidays/__openerp__.py b/addons/hr_holidays/__openerp__.py
index d5a2728c763..070ae0344b0 100644
--- a/addons/hr_holidays/__openerp__.py
+++ b/addons/hr_holidays/__openerp__.py
@@ -21,7 +21,7 @@
{
- 'name': 'Leaves Management',
+ 'name': 'Leave Management',
'version': '1.5',
'author': 'OpenERP SA',
'category': 'Human Resources',
@@ -29,8 +29,8 @@
'summary': 'Holidays, Allocation and Leave Requests',
'website': 'http://www.openerp.com',
'description': """
-This module allows you to manage leaves and leave's requests.
-=============================================================
+This module allows you to manage leaves and leave requests.
+===========================================================
Implements a dashboard for human resource management that includes:
-------------------------------------------------------------------
diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py
index 5840217d7ee..759170224bd 100644
--- a/addons/hr_holidays/hr_holidays.py
+++ b/addons/hr_holidays/hr_holidays.py
@@ -25,6 +25,7 @@ import datetime, time
from itertools import groupby
from operator import itemgetter
+import math
import netsvc
from osv import fields, osv
from tools.translate import _
@@ -94,7 +95,7 @@ class hr_holidays(osv.osv):
_name = "hr.holidays"
_description = "Leave"
_order = "type desc, date_from asc"
- _inherit = ['ir.needaction_mixin', 'mail.thread']
+ _inherit = [ 'mail.thread','ir.needaction_mixin']
def _employee_get(self, cr, uid, context=None):
ids = self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context)
@@ -112,7 +113,7 @@ class hr_holidays(osv.osv):
return result
_columns = {
- 'name': fields.char('Description', required=True, size=64),
+ 'name': fields.char('Description', size=64),
'state': fields.selection([('draft', 'To Submit'), ('cancel', 'Cancelled'),('confirm', 'To Approve'), ('refuse', 'Refused'), ('validate1', 'Second Approval'), ('validate', 'Approved')],
'State', readonly=True, help='The state is set to \'To Submit\', when a holiday request is created.\
\nThe state is \'To Approve\', when holiday request is confirmed by user.\
@@ -123,8 +124,6 @@ class hr_holidays(osv.osv):
'date_to': fields.datetime('End Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'holiday_status_id': fields.many2one("hr.holidays.status", "Leave Type", required=True,readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'employee_id': fields.many2one('hr.employee', "Employee", select=True, invisible=False, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, help='Leave Manager can let this field empty if this leave request/allocation is for every employee'),
- #'manager_id': fields.many2one('hr.employee', 'Leave Manager', invisible=False, readonly=True, help='This area is automatically filled by the user who validate the leave'),
- #'notes': fields.text('Notes',readonly=True, states={'draft':[('readonly',False)]}),
'manager_id': fields.many2one('hr.employee', 'First Approval', invisible=False, readonly=True, help='This area is automatically filled by the user who validate the leave'),
'notes': fields.text('Reasons',readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'number_of_days_temp': fields.float('Number of Days', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
@@ -134,8 +133,8 @@ class hr_holidays(osv.osv):
'parent_id': fields.many2one('hr.holidays', 'Parent'),
'linked_request_ids': fields.one2many('hr.holidays', 'parent_id', 'Linked Requests',),
'department_id':fields.related('employee_id', 'department_id', string='Department', type='many2one', relation='hr.department', readonly=True, store=True),
- 'category_id': fields.many2one('hr.employee.category', "Category", help='Category of Employee'),
- 'holiday_type': fields.selection([('employee','By Employee'),('category','By Employee Category')], 'Allocation Type', help='By Employee: Allocation/Request for individual Employee, By Employee Category: Allocation/Request for group of employees in category', required=True),
+ 'category_id': fields.many2one('hr.employee.category', "Category", help='Category of Employee', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
+ 'holiday_type': fields.selection([('employee','By Employee'),('category','By Employee Category')], 'Allocation Mode', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, help='By Employee: Allocation/Request for individual Employee, By Employee Category: Allocation/Request for group of employees in category', required=True),
'manager_id2': fields.many2one('hr.employee', 'Second Approval', readonly=True, help='This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation)'),
'double_validation': fields.related('holiday_status_id', 'double_validation', type='boolean', relation='hr.holidays.status', string='Apply Double Validation'),
}
@@ -147,16 +146,11 @@ class hr_holidays(osv.osv):
'holiday_type': 'employee'
}
_sql_constraints = [
- ('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL))", "You have to select an employee or a category."),
+ ('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL))", "The employee or employee category of this request is missing."),
('date_check2', "CHECK ( (type='add') OR (date_from <= date_to))", "The start date must be before the end date !"),
('date_check', "CHECK ( number_of_days_temp >= 0 )", "The number of days must be greater than 0 !"),
]
- def create(self, cr, uid, vals, context=None):
- obj_id = super(hr_holidays, self).create(cr, uid, vals, context=context)
- self.create_notificate(cr, uid, [obj_id], context=context)
- return obj_id
-
def _create_resource_leave(self, cr, uid, leaves, context=None):
'''This method will create entry in resource calendar leave object at the time of holidays validated '''
obj_res_leave = self.pool.get('resource.calendar.leaves')
@@ -201,8 +195,8 @@ class hr_holidays(osv.osv):
def unlink(self, cr, uid, ids, context=None):
for rec in self.browse(cr, uid, ids, context=context):
- if rec.state<>'draft':
- raise osv.except_osv(_('Warning!'),_('You cannot delete a leave which is not in draft state !'))
+ if rec.state not in ['draft', 'cancel', 'confirm']:
+ raise osv.except_osv(_('Warning!'),_('You cannot delete a leave which is in %s state!')%(rec.state))
return super(hr_holidays, self).unlink(cr, uid, ids, context)
def onchange_date_from(self, cr, uid, ids, date_to, date_from):
@@ -210,7 +204,7 @@ class hr_holidays(osv.osv):
if date_to and date_from:
diff_day = self._get_number_of_days(date_from, date_to)
result['value'] = {
- 'number_of_days_temp': round(diff_day)+1
+ 'number_of_days_temp': round(math.floor(diff_day))+1
}
return result
result['value'] = {
@@ -244,29 +238,30 @@ class hr_holidays(osv.osv):
self.unlink(cr, uid, to_unlink, context=context)
return True
- def holidays_validate(self, cr, uid, ids, context=None):
+ def holidays_first_validate(self, cr, uid, ids, context=None):
self.check_holidays(cr, uid, ids, context=context)
obj_emp = self.pool.get('hr.employee')
ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)])
manager = ids2 and ids2[0] or False
- self.holidays_validate_notificate(cr, uid, ids, context=context)
+ self.holidays_first_validate_notificate(cr, uid, ids, context=context)
return self.write(cr, uid, ids, {'state':'validate1', 'manager_id': manager})
- def holidays_validate2(self, cr, uid, ids, context=None):
+ def holidays_validate(self, cr, uid, ids, context=None):
self.check_holidays(cr, uid, ids, context=context)
obj_emp = self.pool.get('hr.employee')
ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)])
manager = ids2 and ids2[0] or False
self.write(cr, uid, ids, {'state':'validate'})
data_holiday = self.browse(cr, uid, ids)
- holiday_ids = []
for record in data_holiday:
- if record.holiday_status_id.double_validation:
- holiday_ids.append(record.id)
+ if record.double_validation:
+ self.write(cr, uid, [record.id], {'manager_id2': manager})
+ else:
+ self.write(cr, uid, [record.id], {'manager_id': manager})
if record.holiday_type == 'employee' and record.type == 'remove':
meeting_obj = self.pool.get('crm.meeting')
meeting_vals = {
- 'name': record.name,
+ 'name': record.name or _('Leave Request'),
'categ_ids': record.holiday_status_id.categ_id and [(6,0,[record.holiday_status_id.categ_id.id])] or [],
'duration': record.number_of_days_temp * 8,
'description': record.notes,
@@ -301,25 +296,27 @@ class hr_holidays(osv.osv):
wf_service.trg_validate(uid, 'hr.holidays', leave_id, 'confirm', cr)
wf_service.trg_validate(uid, 'hr.holidays', leave_id, 'validate', cr)
wf_service.trg_validate(uid, 'hr.holidays', leave_id, 'second_validate', cr)
- if holiday_ids:
- self.holidays_valid2_notificate(cr, uid, holiday_ids, context=context)
- self.write(cr, uid, holiday_ids, {'manager_id2': manager})
+ self.holidays_validate_notificate(cr, uid, ids, context=context)
return True
def holidays_confirm(self, cr, uid, ids, context=None):
self.check_holidays(cr, uid, ids, context=context)
+ for record in self.browse(cr, uid, ids, context=context):
+ if record.employee_id and record.employee_id.parent_id and record.employee_id.parent_id.user_id:
+ self.message_subscribe(cr, uid, [record.id], user_ids=[record.employee_id.parent_id.user_id.id], context=context)
self.holidays_confirm_notificate(cr, uid, ids, context=context)
return self.write(cr, uid, ids, {'state':'confirm'})
- def holidays_refuse(self, cr, uid, ids, approval, context=None):
+ def holidays_refuse(self, cr, uid, ids, context=None):
obj_emp = self.pool.get('hr.employee')
ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)])
manager = ids2 and ids2[0] or False
- if approval == 'first_approval':
- self.write(cr, uid, ids, {'state': 'refuse', 'manager_id': manager})
- else:
- self.write(cr, uid, ids, {'state': 'refuse', 'manager_id2': manager})
- self.holidays_refuse_notificate(cr, uid, ids, approval, context=context)
+ for holiday in self.browse(cr, uid, ids, context=context):
+ if holiday.state == 'validate1':
+ self.write(cr, uid, [holiday.id], {'state': 'refuse', 'manager_id': manager})
+ else:
+ self.write(cr, uid, [holiday.id], {'state': 'refuse', 'manager_id2': manager})
+ self.holidays_refuse_notificate(cr, uid, ids, context=context)
self.holidays_cancel(cr, uid, ids, context=context)
return True
@@ -333,7 +330,7 @@ class hr_holidays(osv.osv):
# If a category that created several holidays, cancel all related
wf_service = netsvc.LocalService("workflow")
for request in record.linked_request_ids or []:
- wf_service.trg_validate(uid, 'hr.holidays', request.id, 'cancel', cr)
+ wf_service.trg_validate(uid, 'hr.holidays', request.id, 'refuse', cr)
self._remove_resource_leave(cr, uid, ids, context=context)
return True
@@ -347,68 +344,53 @@ class hr_holidays(osv.osv):
if leaves_rest < record.number_of_days_temp:
raise osv.except_osv(_('Warning!'), _('There are not enough %s allocated for employee %s; please create an allocation request for this leave type.') % (record.holiday_status_id.name, record.employee_id.name))
return True
-
+
# -----------------------------
# OpenChatter and notifications
# -----------------------------
-
- def get_needaction_user_ids(self, cr, uid, ids, context=None):
- result = super(hr_holidays, self).get_needaction_user_ids(cr, uid, ids, context=context)
- for obj in self.browse(cr, uid, ids, context=context):
- if obj.state == 'confirm' and obj.employee_id.parent_id:
- result[obj.id] = [obj.employee_id.parent_id.user_id.id]
- elif obj.state == 'validate1':
- # get group_hr_manager: everyone will be warned of second validation
- res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_hr_manager') or False
- obj_id = res and res[1] or False
- if obj_id:
- hr_manager_group = self.pool.get('res.groups').read(cr, uid, [obj_id], ['users'], context=context)[0]
- result[obj.id] = hr_manager_group['users']
- return result
- def message_get_monitored_follower_fields(self, cr, uid, ids, context=None):
- """ Add 'user_id' and 'manager' to the monitored fields """
- res = super(hr_holidays, self).message_get_monitored_follower_fields(cr, uid, ids, context=context)
- # TODO: add manager
- return res + ['user_id']
-
+ def needaction_domain_get(self, cr, uid, ids, context=None):
+ # to be tested, otherwise convert into employee_id in ...
+ emp_obj = self.pool.get('hr.employee')
+ empids = emp_obj.search(cr, uid, [('parent_id.user_id','=',uid)], context=context)
+ dom = [
+ '&', ('state','=','confirm'),('employee_id', 'in', empids)
+ ]
+ # if this user is a hr.manager, he should do second validations
+ if self.pool.get('res.users').has_group(cr, uid, 'base.group_hr_manager'):
+ dom = ['|'] + dom + [ ('state','=','validate1') ]
+ return dom
+
def create_notificate(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
- self.message_append_note(cr, uid, ids, _('System notification'),
- _("The %s request has been created and is waiting confirmation.")
- % ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context)
+ self.message_post(cr, uid, ids,
+ _("The request has been created and is waiting confirmation."), context=context)
return True
def holidays_confirm_notificate(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids):
- self.message_append_note(cr, uid, [obj.id], _('System notification'),
- _("The %s request has been confirmed and is waiting for validation by the manager.")
- % ('leave' if obj.type == 'remove' else 'allocation',), type='notification')
+ self.message_post(cr, uid, [obj.id],
+ _("The request has been submitted and is waiting for validation by the manager."), context=context)
+ def holidays_first_validate_notificate(self, cr, uid, ids, context=None):
+ for obj in self.browse(cr, uid, ids, context=context):
+ self.message_post(cr, uid, [obj.id],
+ _("The request has been approved. A second validation is necessary and is now pending."), context=context)
+
def holidays_validate_notificate(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids):
- if obj.holiday_status_id.double_validation:
- self.message_append_note(cr, uid, [obj.id], _('System notification'),
- _("The %s request has been approved. A second validation is necessary and is now pending.")
- % ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context)
+ if obj.double_validation:
+ self.message_post(cr, uid, [obj.id],
+ _("The request has been double validated. The validation process is now over."), context=context)
else:
- self.message_append_note(cr, uid, [obj.id], _('System notification'),
- _("The %s request has been approved. The validation process is now over.")
- % ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context)
+ self.message_post(cr, uid, [obj.id],
+ _("The request has been approved. The validation process is now over."), context=context)
- def holidays_valid2_notificate(self, cr, uid, ids, context=None):
+ def holidays_refuse_notificate(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids):
- self.message_append_note(cr, uid, [obj.id], _('System notification'),
- _("The %s request has been double validated. The validation process is now over.")
- % ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context)
-
- def holidays_refuse_notificate(self, cr, uid, ids, approval, context=None):
- for obj in self.browse(cr, uid, ids):
- self.message_append_note(cr, uid, [obj.id], _('System notification'),
- _("The %s request has been refused. The validation process is now over.")
- % ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context)
-
-hr_holidays()
+ self.message_post(cr, uid, [obj.id],
+ _("The request has been refused. The validation process is now over."), context=context)
+
class resource_calendar_leaves(osv.osv):
_inherit = "resource.calendar.leaves"
diff --git a/addons/hr_holidays/hr_holidays_data.xml b/addons/hr_holidays/hr_holidays_data.xml
index 9db0f2e987a..1e534093eb3 100644
--- a/addons/hr_holidays/hr_holidays_data.xml
+++ b/addons/hr_holidays/hr_holidays_data.xml
@@ -13,12 +13,15 @@
-
+
@@ -327,7 +298,7 @@
-
-
+
+
@@ -208,29 +208,28 @@
+
i
@@ -340,7 +339,7 @@
-
-
+
+
+
diff --git a/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml b/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml
index f4f9326890f..849c2fa7caa 100644
--- a/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml
+++ b/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml
@@ -26,7 +26,7 @@
Now, at the time of login, I create Attendances and perform "Sign In" action.
-
!python {model: hr_timesheet_sheet.sheet}: |
- self.sign_in(cr, uid, [ref('hr_timesheet_sheet_sheet_deddk0')], {})
+ self.attendance_action_change(cr, uid, [ref('hr_timesheet_sheet_sheet_deddk0')], {})
task_work = self.search(cr, uid, [("name","=","Quentin Paolinon")],context)[0]
task_ids = self.browse(cr, uid, task_work, context)
assert task_ids.state == "draft", "I State In Timesheet"
@@ -69,7 +69,7 @@
- account_id: account.analytic_agrolait
date: !eval time.strftime('%Y-%m-%d')
name: 'Develop yaml for hr module'
- user_id: base.user_qdp
+ user_id: base.user_demo
unit_amount: 6.00
amount: -90.00
product_id: product.product_product_consultant
@@ -97,7 +97,7 @@
date: !eval time.strftime('%Y-%m-%d')
name: 'Develop yaml for hr module'
unit_amount: 2.00
- user_id: base.user_qdp
+ user_id: base.user_demo
amount: -90.00
product_id: product.product_product_consultant
general_account_id: account.a_expense
diff --git a/addons/idea/idea.py b/addons/idea/idea.py
index 3be4dd2e28d..e2e5ec50ddf 100644
--- a/addons/idea/idea.py
+++ b/addons/idea/idea.py
@@ -67,21 +67,21 @@ class idea_idea(osv.osv):
def idea_cancel(self, cr, uid, ids, context={}):
self.write(cr, uid, ids, { 'state': 'cancel' })
- self.message_append_note(cr, uid, ids, body=_('Idea canceled.'), context=context)
+ self.message_post(cr, uid, ids, body=_('Idea canceled.'), context=context)
return True
def idea_open(self, cr, uid, ids, context={}):
self.write(cr, uid, ids, { 'state': 'open'})
- self.message_append_note(cr, uid, ids, body=_('Idea accepted.'), context=context)
+ self.message_post(cr, uid, ids, body=_('Idea accepted.'), context=context)
return True
def idea_close(self, cr, uid, ids, context={}):
- self.message_append_note(cr, uid, ids, body=_('Idea done.'), context=context)
+ self.message_post(cr, uid, ids, body=_('Idea done.'), context=context)
self.write(cr, uid, ids, { 'state': 'close' })
return True
def idea_draft(self, cr, uid, ids, context={}):
- self.message_append_note(cr, uid, ids, body=_('Idea reset to draft.'), context=context)
+ self.message_post(cr, uid, ids, body=_('Idea reset to draft.'), context=context)
self.write(cr, uid, ids, { 'state': 'draft' })
return True
idea_idea()
diff --git a/addons/import_base/i18n/nb.po b/addons/import_base/i18n/nb.po
new file mode 100644
index 00000000000..62b30d04da6
--- /dev/null
+++ b/addons/import_base/i18n/nb.po
@@ -0,0 +1,105 @@
+# Norwegian Bokmal translation for openobject-addons
+# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
+# This file is distributed under the same license as the openobject-addons package.
+# FIRST AUTHOR \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "E-mail Address"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:payment.advice.report,employee_bank_no:0
+msgid "Employee Bank Account"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Payment Advices which are in draft state"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Title"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "Payment Advice from"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_yearly_salary_detail
+msgid "Hr Salary Employee By Category Report"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Employees Salary Details"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Allowances with Basic:"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Department"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Deductions:"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "A/C no."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.contract,driver_salay:0
+msgid "Driver Salary"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_yearly_salary_detail
+#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.yearly_salary
+#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_yearly_salary_detail
+msgid "Yearly Salary by Employee"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.act_window,name:l10n_in_hr_payroll.act_hr_emp_payslip_list
+msgid "Payslips"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "March"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "("
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: field:hr.payroll.advice,company_id:0
+#: field:hr.payroll.advice.line,company_id:0
+#: view:payment.advice.report:0
+#: field:payment.advice.report,company_id:0
+msgid "Company"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "The Manager"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Letter Details"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid ","
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Set to Draft"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.employee,number_of_year:0
+msgid "Total years of work experience"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "to"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "Total :"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payslip.run,available_advice:0
+msgid "Made Payment Advice?"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Advices which are paid using NEFT transfer"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.contract,tds:0
+msgid "Amount for Tax Deduction at Source"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip
+msgid "Pay Slip"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+#: field:payment.advice.report,day:0
+msgid "Day"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Month of Payment Advices"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: constraint:hr.payslip:0
+msgid "Payslip 'Date From' must be before 'Date To'."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,batch_id:0
+msgid "Batch"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Code"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Other Information"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:hr.payroll.advice,state:0
+#: selection:payment.advice.report,state:0
+msgid "Cancelled"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "For"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Details by Salary Rule Category:"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.contract,voluntary_provident_fund:0
+msgid "VPF computed as percentage(%)"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,number:0
+#: report:paylip.details.in:0
+msgid "Reference"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: view:payment.advice.report:0
+msgid "Group By..."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.contract,medical_insurance:0
+msgid "Medical Insurance"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Identification No"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:hr.payroll.advice,state:0
+#: selection:payment.advice.report,state:0
+msgid "Confirmed"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+#: report:salary.employee.bymonth:0
+msgid "From"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice.line,bysal:0
+#: field:payment.advice.report,bysal:0
+#: report:payroll.advice:0
+msgid "By Salary"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: view:payment.advice.report:0
+msgid "Confirm"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,chaque_nos:0
+#: field:payment.advice.report,cheque_nos:0
+msgid "Cheque Numbers"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: constraint:res.company:0
+msgid "Error! You can not create recursive companies."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_salary_employee_month
+#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.hr_salary_employee_bymonth
+#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_salary_employee_month
+msgid "Yearly Salary by Head"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184
+#, python-format
+msgid "You can not confirm Payment advice without advice lines."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,state:0
+#: field:payment.advice.report,state:0
+msgid "State"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "Yours Sincerely"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.contract,medical_insurance:0
+msgid "Deduction towards company provided medical insurance"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice_line
+msgid "Bank Advice Lines"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Email"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.payslip.run,available_advice:0
+msgid "If this box is checked which means that Payment Advice exists for current batch"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257
+#, python-format
+msgid "Error !"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.salary.employee.month:0
+#: view:yearly.salary.detail:0
+msgid "Print"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip_run
+msgid "Payslip Batches"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice.line,debit_credit:0
+#: report:payroll.advice:0
+msgid "C/D"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.employee.bymonth:0
+msgid "Yearly Salary Details"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payroll_advice
+msgid "Print Advice"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,line_ids:0
+msgid "Employee Salary"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "July"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:res.company:0
+msgid "Configuration"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_view_hr_bank_advice_tree
+#: model:ir.ui.menu,name:l10n_in_hr_payroll.hr_menu_payment_advice
+msgid "Payment Advices"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_payment_advice_report_all
+#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_reporting_payment_advice
+#: view:payment.advice.report:0
+msgid "Advices Analysis"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.salary.employee.month:0
+msgid "This wizard will print report which displays employees break-up of Net Head for a specified dates."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice.line,ifsc:0
+msgid "IFSC"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Date To"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.contract,tds:0
+msgid "TDS"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.employee,join_date:0
+msgid "Join Date"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Confirm Advices"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: constraint:hr.contract:0
+msgid "Error! Contract start-date must be less than contract end-date."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:res.company,dearness_allowance:0
+msgid "Dearness Allowance"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "August"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.contract:0
+msgid "Deduction"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Search Payment advice"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "SI. No."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Payment Advices which are in confirm state"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "December"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Confirm Sheet"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+#: field:payment.advice.report,month:0
+msgid "Month"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Employee Code"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:hr.contract,city_type:0
+msgid "Non Metro"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.salary.employee.month:0
+#: view:yearly.salary.detail:0
+msgid "or"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_salary_employee_month
+msgid "Hr Salary Employee By Month Report"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.salary.employee.month,category_id:0
+msgid "Category"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240
+#, python-format
+msgid "Payment advice already exists for %s, 'Set to Draft' to create a new advice."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payslip.run:0
+msgid "To Advice"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.employee,number_of_year:0
+msgid "No. of Years of Service"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Note"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Salary Rule Category"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: selection:hr.payroll.advice,state:0
+#: view:payment.advice.report:0
+#: selection:payment.advice.report,state:0
+msgid "Draft"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Date From"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.contract,voluntary_provident_fund:0
+msgid "Voluntary Provident Fund"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Employee Name"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_payment_advice_report
+msgid "Payment Advice Analysis"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: view:payment.advice.report:0
+msgid "Status"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.contract,city_type:0
+msgid "Type of City"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:res.company,dearness_allowance:0
+msgid "Check this box if your company provide Dearness Allowance to employee"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice.line,ifsc_code:0
+#: field:payment.advice.report,ifsc_code:0
+#: report:payroll.advice:0
+msgid "IFSC Code"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "June"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+#: field:payment.advice.report,nbr:0
+msgid "# Payment Lines"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payslip_details_report
+msgid "PaySlip Details"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Payment Lines"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,date:0
+#: field:payment.advice.report,date:0
+msgid "Date"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "November"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Extended Filters..."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.act_window,help:l10n_in_hr_payroll.action_payment_advice_report_all
+msgid "This report performs analysis on Payment Advices"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "October"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+#: report:salary.detail.byyear:0
+msgid "Designation"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "January"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:yearly.salary.detail:0
+msgid "Pay Head Employee Breakup"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_res_company
+msgid "Companies"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+#: report:payroll.advice:0
+msgid "Authorized Signature"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_contract
+msgid "Contract"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice.line:0
+msgid "Advice Lines"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "To,"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.contract,driver_salay:0
+msgid "Check this box if you provide allowance for driver"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: field:hr.payroll.advice.line,advice_id:0
+#: field:hr.payslip,advice_id:0
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice
+msgid "Bank Advice"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Other No."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Draft Advices"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.payroll.advice,neft:0
+msgid "Check this box if your company use online transfer for salary"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "To"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:payment.advice.report,number:0
+msgid "Number"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "September"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: view:hr.salary.employee.month:0
+#: view:yearly.salary.detail:0
+msgid "Cancel"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Day of Payment Advices"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: constraint:hr.employee:0
+msgid "Error! You cannot create recursive hierarchy of Employee(s)."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:yearly.salary.detail:0
+msgid "This wizard will print report which display a pay head employee breakup for a specified dates."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payslip.run:0
+msgid "Payslip Batches ready to be Adviced"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Pay Slip Details"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Total Salary"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice.line,employee_id:0
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_employee
+#: view:payment.advice.report:0
+#: field:payment.advice.report,employee_id:0
+msgid "Employee"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Compute Advice"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.employee,join_date:0
+msgid "Joining date of employee"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "Dear Sir/Madam,"
+msgstr "Dear Sir/Madam,"
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,note:0
+msgid "Description"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid ")"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:res.company:0
+msgid "Payroll"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "NEFT"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+#: report:salary.detail.byyear:0
+msgid "Address"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: field:hr.payroll.advice,bank_id:0
+#: view:payment.advice.report:0
+#: field:payment.advice.report,bank_id:0
+#: report:payroll.advice:0
+#: report:salary.detail.byyear:0
+msgid "Bank"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.salary.employee.month,end_date:0
+#: field:yearly.salary.detail,date_to:0
+msgid "End Date"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "February"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: sql_constraint:res.company:0
+msgid "The company name must be unique !"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: field:hr.payroll.advice,name:0
+#: report:paylip.details.in:0
+#: field:payment.advice.report,name:0
+#: report:salary.employee.bymonth:0
+msgid "Name"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:hr.contract,city_type:0
+msgid "Metro"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.salary.employee.month:0
+#: field:hr.salary.employee.month,employee_ids:0
+#: view:yearly.salary.detail:0
+#: field:yearly.salary.detail,employee_ids:0
+msgid "Employees"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Bank Account"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "April"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "Name of the Employe"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257
+#, python-format
+msgid "Please define bank account for the %s employee"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.salary.employee.month,start_date:0
+#: field:yearly.salary.detail,date_from:0
+msgid "Start Date"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.contract:0
+msgid "Allowance"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.payroll.advice,bank_id:0
+msgid "Select the Bank from which the salary is going to be paid"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.salary.employee.month:0
+msgid "Employee Pay Head Breakup"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Phone No."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Credit"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice.line,name:0
+#: report:payroll.advice:0
+msgid "Bank Account No."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.payroll.advice,date:0
+msgid "Advice Date is used to search Payslips"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "May"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payslip.run:0
+msgid "Create Advice"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+#: field:payment.advice.report,year:0
+msgid "Year"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,neft:0
+#: field:payment.advice.report,neft:0
+msgid "NEFT Transaction"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+#: report:salary.detail.byyear:0
+#: report:salary.employee.bymonth:0
+msgid "Total"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "form period"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Year of Payment Advices"
+msgstr ""
\ No newline at end of file
diff --git a/addons/l10n_in_hr_payroll/i18n/l10n_in_hr_payroll.pot b/addons/l10n_in_hr_payroll/i18n/l10n_in_hr_payroll.pot
new file mode 100644
index 00000000000..552f85b5997
--- /dev/null
+++ b/addons/l10n_in_hr_payroll/i18n/l10n_in_hr_payroll.pot
@@ -0,0 +1,933 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+# * l10n_in_hr_payroll
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 7.0alpha\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-08-17 06:46+0000\n"
+"PO-Revision-Date: 2012-08-17 06:46+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "E-mail Address"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:payment.advice.report,employee_bank_no:0
+msgid "Employee Bank Account"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Payment Advices which are in draft state"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Title"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "Payment Advice from"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_yearly_salary_detail
+msgid "Hr Salary Employee By Category Report"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Employees Salary Details"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Allowances with Basic:"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Department"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Deductions:"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "A/C no."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.contract,driver_salay:0
+msgid "Driver Salary"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_yearly_salary_detail
+#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.yearly_salary
+#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_yearly_salary_detail
+msgid "Yearly Salary by Employee"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.act_window,name:l10n_in_hr_payroll.act_hr_emp_payslip_list
+msgid "Payslips"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "March"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "("
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: field:hr.payroll.advice,company_id:0
+#: field:hr.payroll.advice.line,company_id:0
+#: view:payment.advice.report:0
+#: field:payment.advice.report,company_id:0
+msgid "Company"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "The Manager"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Letter Details"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid ","
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Set to Draft"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.employee,number_of_year:0
+msgid "Total years of work experience"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "to"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "Total :"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payslip.run,available_advice:0
+msgid "Made Payment Advice?"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Advices which are paid using NEFT transfer"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.contract,tds:0
+msgid "Amount for Tax Deduction at Source"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip
+msgid "Pay Slip"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+#: field:payment.advice.report,day:0
+msgid "Day"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Month of Payment Advices"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: constraint:hr.payslip:0
+msgid "Payslip 'Date From' must be before 'Date To'."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,batch_id:0
+msgid "Batch"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Code"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Other Information"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:hr.payroll.advice,state:0
+#: selection:payment.advice.report,state:0
+msgid "Cancelled"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "For"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Details by Salary Rule Category:"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.contract,voluntary_provident_fund:0
+msgid "VPF computed as percentage(%)"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,number:0
+#: report:paylip.details.in:0
+msgid "Reference"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: view:payment.advice.report:0
+msgid "Group By..."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.contract,medical_insurance:0
+msgid "Medical Insurance"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Identification No"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:hr.payroll.advice,state:0
+#: selection:payment.advice.report,state:0
+msgid "Confirmed"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+#: report:salary.employee.bymonth:0
+msgid "From"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice.line,bysal:0
+#: field:payment.advice.report,bysal:0
+#: report:payroll.advice:0
+msgid "By Salary"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: view:payment.advice.report:0
+msgid "Confirm"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,chaque_nos:0
+#: field:payment.advice.report,cheque_nos:0
+msgid "Cheque Numbers"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: constraint:res.company:0
+msgid "Error! You can not create recursive companies."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_salary_employee_month
+#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.hr_salary_employee_bymonth
+#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_salary_employee_month
+msgid "Yearly Salary by Head"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184
+#, python-format
+msgid "You can not confirm Payment advice without advice lines."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,state:0
+#: field:payment.advice.report,state:0
+msgid "State"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "Yours Sincerely"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.contract,medical_insurance:0
+msgid "Deduction towards company provided medical insurance"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice_line
+msgid "Bank Advice Lines"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Email"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.payslip.run,available_advice:0
+msgid "If this box is checked which means that Payment Advice exists for current batch"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257
+#, python-format
+msgid "Error !"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.salary.employee.month:0
+#: view:yearly.salary.detail:0
+msgid "Print"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip_run
+msgid "Payslip Batches"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice.line,debit_credit:0
+#: report:payroll.advice:0
+msgid "C/D"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.employee.bymonth:0
+msgid "Yearly Salary Details"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payroll_advice
+msgid "Print Advice"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,line_ids:0
+msgid "Employee Salary"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "July"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:res.company:0
+msgid "Configuration"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_view_hr_bank_advice_tree
+#: model:ir.ui.menu,name:l10n_in_hr_payroll.hr_menu_payment_advice
+msgid "Payment Advices"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_payment_advice_report_all
+#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_reporting_payment_advice
+#: view:payment.advice.report:0
+msgid "Advices Analysis"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.salary.employee.month:0
+msgid "This wizard will print report which displays employees break-up of Net Head for a specified dates."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice.line,ifsc:0
+msgid "IFSC"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Date To"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.contract,tds:0
+msgid "TDS"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.employee,join_date:0
+msgid "Join Date"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Confirm Advices"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: constraint:hr.contract:0
+msgid "Error! Contract start-date must be less than contract end-date."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:res.company,dearness_allowance:0
+msgid "Dearness Allowance"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "August"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.contract:0
+msgid "Deduction"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Search Payment advice"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "SI. No."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Payment Advices which are in confirm state"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "December"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Confirm Sheet"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+#: field:payment.advice.report,month:0
+msgid "Month"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Employee Code"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:hr.contract,city_type:0
+msgid "Non Metro"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.salary.employee.month:0
+#: view:yearly.salary.detail:0
+msgid "or"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_salary_employee_month
+msgid "Hr Salary Employee By Month Report"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.salary.employee.month,category_id:0
+msgid "Category"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240
+#, python-format
+msgid "Payment advice already exists for %s, 'Set to Draft' to create a new advice."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payslip.run:0
+msgid "To Advice"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.employee,number_of_year:0
+msgid "No. of Years of Service"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Note"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Salary Rule Category"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: selection:hr.payroll.advice,state:0
+#: view:payment.advice.report:0
+#: selection:payment.advice.report,state:0
+msgid "Draft"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Date From"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.contract,voluntary_provident_fund:0
+msgid "Voluntary Provident Fund"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Employee Name"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_payment_advice_report
+msgid "Payment Advice Analysis"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: view:payment.advice.report:0
+msgid "Status"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.contract,city_type:0
+msgid "Type of City"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:res.company,dearness_allowance:0
+msgid "Check this box if your company provide Dearness Allowance to employee"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice.line,ifsc_code:0
+#: field:payment.advice.report,ifsc_code:0
+#: report:payroll.advice:0
+msgid "IFSC Code"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "June"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+#: field:payment.advice.report,nbr:0
+msgid "# Payment Lines"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payslip_details_report
+msgid "PaySlip Details"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Payment Lines"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,date:0
+#: field:payment.advice.report,date:0
+msgid "Date"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "November"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Extended Filters..."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.actions.act_window,help:l10n_in_hr_payroll.action_payment_advice_report_all
+msgid "This report performs analysis on Payment Advices"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "October"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+#: report:salary.detail.byyear:0
+msgid "Designation"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "January"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:yearly.salary.detail:0
+msgid "Pay Head Employee Breakup"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_res_company
+msgid "Companies"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+#: report:payroll.advice:0
+msgid "Authorized Signature"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_contract
+msgid "Contract"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice.line:0
+msgid "Advice Lines"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "To,"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.contract,driver_salay:0
+msgid "Check this box if you provide allowance for driver"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: field:hr.payroll.advice.line,advice_id:0
+#: field:hr.payslip,advice_id:0
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice
+msgid "Bank Advice"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Other No."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Draft Advices"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.payroll.advice,neft:0
+msgid "Check this box if your company use online transfer for salary"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "To"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:payment.advice.report,number:0
+msgid "Number"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "September"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: view:hr.salary.employee.month:0
+#: view:yearly.salary.detail:0
+msgid "Cancel"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Day of Payment Advices"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: constraint:hr.employee:0
+msgid "Error! You cannot create recursive hierarchy of Employee(s)."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:yearly.salary.detail:0
+msgid "This wizard will print report which display a pay head employee breakup for a specified dates."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payslip.run:0
+msgid "Payslip Batches ready to be Adviced"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Pay Slip Details"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Total Salary"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice.line,employee_id:0
+#: model:ir.model,name:l10n_in_hr_payroll.model_hr_employee
+#: view:payment.advice.report:0
+#: field:payment.advice.report,employee_id:0
+msgid "Employee"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+msgid "Compute Advice"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.employee,join_date:0
+msgid "Joining date of employee"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "Dear Sir/Madam,"
+msgstr "Dear Sir/Madam,"
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,note:0
+msgid "Description"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid ")"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:res.company:0
+msgid "Payroll"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "NEFT"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+#: report:salary.detail.byyear:0
+msgid "Address"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: field:hr.payroll.advice,bank_id:0
+#: view:payment.advice.report:0
+#: field:payment.advice.report,bank_id:0
+#: report:payroll.advice:0
+#: report:salary.detail.byyear:0
+msgid "Bank"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.salary.employee.month,end_date:0
+#: field:yearly.salary.detail,date_to:0
+msgid "End Date"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "February"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: sql_constraint:res.company:0
+msgid "The company name must be unique !"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payroll.advice:0
+#: field:hr.payroll.advice,name:0
+#: report:paylip.details.in:0
+#: field:payment.advice.report,name:0
+#: report:salary.employee.bymonth:0
+msgid "Name"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:hr.contract,city_type:0
+msgid "Metro"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.salary.employee.month:0
+#: field:hr.salary.employee.month,employee_ids:0
+#: view:yearly.salary.detail:0
+#: field:yearly.salary.detail,employee_ids:0
+msgid "Employees"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Bank Account"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "April"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "Name of the Employe"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158
+#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257
+#, python-format
+msgid "Please define bank account for the %s employee"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.salary.employee.month,start_date:0
+#: field:yearly.salary.detail,date_from:0
+msgid "Start Date"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.contract:0
+msgid "Allowance"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.payroll.advice,bank_id:0
+msgid "Select the Bank from which the salary is going to be paid"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.salary.employee.month:0
+msgid "Employee Pay Head Breakup"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:salary.detail.byyear:0
+msgid "Phone No."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+msgid "Credit"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice.line,name:0
+#: report:payroll.advice:0
+msgid "Bank Account No."
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: help:hr.payroll.advice,date:0
+msgid "Advice Date is used to search Payslips"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: selection:payment.advice.report,month:0
+msgid "May"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:hr.payslip.run:0
+msgid "Create Advice"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+#: field:payment.advice.report,year:0
+msgid "Year"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: field:hr.payroll.advice,neft:0
+#: field:payment.advice.report,neft:0
+msgid "NEFT Transaction"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:paylip.details.in:0
+#: report:salary.detail.byyear:0
+#: report:salary.employee.bymonth:0
+msgid "Total"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: report:payroll.advice:0
+msgid "form period"
+msgstr ""
+
+#. module: l10n_in_hr_payroll
+#: view:payment.advice.report:0
+msgid "Year of Payment Advices"
+msgstr ""
\ No newline at end of file
diff --git a/addons/l10n_in_hr_payroll/i18n/ta.po b/addons/l10n_in_hr_payroll/i18n/ta.po
new file mode 100644
index 00000000000..98d00c76a33
--- /dev/null
+++ b/addons/l10n_in_hr_payroll/i18n/ta.po
@@ -0,0 +1,933 @@
+# Tamil Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+# * l10n_in_hr_payroll
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 7.0alpha\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-08-17 06:46+0000\n"
+"PO-Revision-Date: 2012-08-17 06:46+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: Tamil
+
+
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
+ |
+
@@ -23,20 +25,16 @@
+
-
-
+
+
+ by
+
- by
- ' : ' '; - return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2'); - }, - - /* Add a prefix before each new line of the original string */ - do_text_quote: function (str, prefix) { - return str.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2' + prefix || '> '); - }, - - /** - * Replaces some expressions - * - @login - shorcut to link to a res.user, given its login - * - [ir.attachment,3|My Label] - shortcut to an internal - * document + /** Replaces some expressions * - :name - shortcut to an image */ do_replace_expressions: function (string) { - var self = this; var icon_list = ['al', 'pinky'] - /* shortcut to user: @login */ - var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g); - var regex_res = regex_login.exec(string); - while (regex_res != null) { - var login = regex_res[2]; - string = string.replace(regex_res[0], regex_res[1] + '@' + login + ''); - regex_res = regex_login.exec(string); - } - /* shortcut for internal document */ - var regex_login = new RegExp(/(^|\s)\[(\w+).(\w+),(\d)\|*((\w|[@ .,])*)\]/g); - var regex_res = regex_login.exec(string); - while (regex_res != null) { - var res_model = regex_res[2] + '.' + regex_res[3]; - var res_id = regex_res[4]; - if (! regex_res[5]) { - var label = res_model + ':' + res_id } - else { - var label = regex_res[5]; - } - string = string.replace(regex_res[0], regex_res[1] + ' 0 && this.params.is_wall); - this.display.show_delete = ! this.params.is_wall; - this.display.show_hide = this.params.is_wall; - this.display.show_reply_by_email = ! this.params.is_wall; - this.display.show_more = (this.params.thread_level == 0); + this.ds_thread = new session.web.DataSetSearch(this, this.context.default_model); + this.ds_notification = new session.web.DataSetSearch(this, 'mail.notification'); + this.ds_message = new session.web.DataSetSearch(this, 'mail.message'); }, start: function() { + // TDE TODO: check for deferred, not sure it is correct this._super.apply(this, arguments); - // add events this.bind_events(); - // display user, fetch comments - this.display_current_user(); - if (this.params.records) var display_done = this.display_comments_from_parameters(this.params.records); - else var display_done = this.init_comments(); - // customize display - $.when(display_done).then(this.proxy('do_customize_display')); + // fetch and display message, using message_ids if set + var display_done = $.when(this.message_fetch(true, [], {})).then(this.proxy('do_customize_display')); // add message composition form view - if (this.display.show_post_comment) { + if (this.options.show_header_compose && this.options.use_composer) { var compose_done = this.instantiate_composition_form(); } return display_done && compose_done; }, - /** - * Override-hack of do_action: automatically reload the chatter. - * Normally it should be called only when clicking on 'Post/Send' - * in the composition form. */ - do_action: function(action, on_close) { - this.init_comments(); - if (this.compose_message_widget) { - this.compose_message_widget.reinit(); } - return this._super(action, on_close); - }, - - instantiate_composition_form: function(mode, email_mode, formatting, msg_id, context) { - if (this.compose_message_widget) { - this.compose_message_widget.destroy(); - } - this.compose_message_widget = new mail.ComposeMessage(this, { - 'extended_mode': false, 'uid': this.params.uid, 'res_model': this.params.res_model, - 'res_id': this.params.res_id, 'mode': mode || 'comment', 'msg_id': msg_id, - 'email_mode': email_mode || false, 'formatting': formatting || false, - 'context': context || false } ); - var composition_node = this.$el.find('div.oe_mail_thread_action'); - composition_node.empty(); - var compose_done = this.compose_message_widget.appendTo(composition_node); - return compose_done; - }, - + /** Customize the display + * - show_header_compose: show the composition form in the header */ do_customize_display: function() { - if (this.display.show_post_comment) { this.$el.find('div.oe_mail_thread_action').eq(0).show(); } + this.display_user_avatar(); + if (this.options.show_header_compose) { + this.$el.find('div.oe_mail_thread_action').eq(0).show(); + } }, /** @@ -596,171 +323,180 @@ openerp.mail = function(session) { * in the function. */ bind_events: function() { var self = this; - // generic events from Chatter Mixin - mail.ChatterUtils.bind_events(this); - // event: click on 'more' at bottom of thread - this.$el.find('button.oe_mail_button_more').click(function () { - self.do_more(); - }); + // event: click on 'More' at bottom of thread + this.$el.on('click', 'button.oe_mail_button_more', this.do_message_fetch_more); // event: writing in basic textarea of composition form (quick reply) this.$el.find('textarea.oe_mail_compose_textarea').keyup(function (event) { var charCode = (event.which) ? event.which : window.event.keyCode; if (event.shiftKey && charCode == 13) { this.value = this.value+"\n"; } - else if (charCode == 13) { return self.do_comment(); } + else if (charCode == 13) { return self.message_post(); } }); // event: click on 'Reply' in msg - this.$el.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_reply', 'click', function (event) { - var act_dom = $(this).parents('div.oe_mail_thread_display').find('div.oe_mail_thread_action:first'); + this.$el.on('click', 'a.oe_mail_msg_reply', function (event) { + var act_dom = $(this).parents('li.oe_mail_thread_msg').eq(0).find('div.oe_mail_thread_action:first'); act_dom.toggle(); - event.preventDefault(); }); - // event: click on 'attachment(s)' in msg - this.$el.delegate('a.oe_mail_msg_view_attachments', 'click', function (event) { + // event: click on 'Attachment(s)' in msg + this.$el.on('click', 'a.oe_mail_msg_view_attachments', function (event) { var act_dom = $(this).parent().parent().parent().find('.oe_mail_msg_attachments'); act_dom.toggle(); - event.preventDefault(); }); // event: click on 'Delete' in msg side menu - this.$el.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_delete', 'click', function (event) { - if (! confirm(_t("Do you really want to delete this message?"))) { return false; } - var msg_id = event.srcElement.dataset.id; - if (! msg_id) return false; - var call_defer = self.ds_msg.unlink([parseInt(msg_id)]); - $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).hide(); - if (self.params.thread_level > 0) { - $(event.srcElement).parents('.oe_mail_thread').eq(0).hide(); - } - event.preventDefault(); - return call_defer; - }); + this.$el.on('click', 'a.oe_mail_msg_delete', this.on_message_delete); // event: click on 'Hide' in msg side menu - this.$el.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_hide', 'click', function (event) { - if (! confirm(_t("Do you really want to hide this thread ?"))) { return false; } - var msg_id = event.srcElement.dataset.id; - if (! msg_id) return false; - var call_defer = self.ds.call('message_remove_pushed_notifications', [[self.params.res_id], [parseInt(msg_id)], true]); - $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).hide(); - if (self.params.thread_level > 0) { - $(event.srcElement).parents('.oe_mail_thread').eq(0).hide(); - } - event.preventDefault(); - return call_defer; - }); - // event: click on "Reply" in msg side menu (email style) - this.$el.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_reply_by_email', 'click', function (event) { + this.$el.on('click', 'a.oe_mail_msg_hide', this.on_message_read); + // event: click on 'Reply by email' in msg side menu + this.$el.on('click', 'a.oe_mail_msg_reply_by_email', function (event) { + if (! self.compose_message_widget) return true; var msg_id = event.srcElement.dataset.msg_id; - var email_mode = (event.srcElement.dataset.type == 'email'); - var formatting = (event.srcElement.dataset.formatting == 'html'); if (! msg_id) return false; - self.instantiate_composition_form('reply', email_mode, formatting, msg_id); - event.preventDefault(); + self.compose_message_widget.refresh({ + 'default_composition_mode': 'reply', + 'default_parent_id': parseInt(msg_id), + 'default_content_subtype': 'html'} ); }); }, - - destroy: function () { - this._super.apply(this, arguments); - }, - - init_comments: function() { - var self = this; - this.params.offset = 0; - this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; - this.$el.find('div.oe_mail_thread_display').empty(); - var domain = this.get_fetch_domain(this.comments_structure); - return this.fetch_comments(this.params.limit, this.params.offset, domain).then(); - }, - - fetch_comments: function (limit, offset, domain) { - var self = this; - var defer = this.ds.call('message_read', [[this.params.res_id], (this.params.thread_level > 0), (this.comments_structure['root_ids']), - (limit+1) || (this.params.limit+1), offset||this.params.offset, domain||undefined ]).then(function (records) { - if (records.length <= self.params.limit) self.display.show_more = false; - // else { self.display.show_more = true; records.pop(); } - // else { self.display.show_more = true; records.splice(0, 1); } - else { self.display.show_more = true; } - self.display_comments(records); - // TODO: move to customize display - if (self.display.show_more == true) self.$el.find('div.oe_mail_thread_more:last').show(); - else self.$el.find('div.oe_mail_thread_more:last').hide(); - }); - - return defer; - }, - display_comments_from_parameters: function (records) { - if (records.length > 0 && records.length < (records[0].child_ids.length+1) ) this.display.show_more = true; - else this.display.show_more = false; - var defer = this.display_comments(records); - // TODO: move to customize display - if (this.display.show_more == true) $('div.oe_mail_thread_more').eq(-2).show(); - else $('div.oe_mail_thread_more').eq(-2).hide(); - return defer; + on_message_delete: function (event) { + if (! confirm(_t("Do you really want to delete this message?"))) { return false; } + var msg_id = event.srcElement.dataset.id; + if (! msg_id) return false; + $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).remove(); + return this.ds_message.unlink([parseInt(msg_id)]); }, - - display_comments: function (records) { - var self = this; - // sort the records - mail.ChatterUtils.records_struct_add_records(this.comments_structure, records, this.params.parent_id); - //build attachments download urls and compute time-relative from dates - for (var k in records) { - records[k].timerelative = $.timeago(records[k].date); - if (records[k].attachments) { - for (var l in records[k].attachments) { - var url = self.session.origin + '/web/binary/saveas?session_id=' + self.session.session_id + '&model=ir.attachment&field=datas&filename_field=datas_fname&id='+records[k].attachments[l].id; - records[k].attachments[l].url = url; - } - } + + on_message_read: function (event) { + //TDE: TODO + var msg_id = event.srcElement.dataset.id; + if (! msg_id) return false; + $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).remove(); + return this.ds_notification.call('set_message_read', [parseInt(msg_id)]); + }, + + /** + * Override-hack of do_action: automatically reload the chatter. + * Normally it should be called only when clicking on 'Post/Send' + * in the composition form. */ + do_action: function(action, on_close) { + //TDE: TODO: instead of reloading, push the message ? + this.message_clean(); + this.message_fetch(); + if (this.compose_message_widget) { + this.compose_message_widget.refresh({ + 'default_composition_mode': 'comment', + 'default_parent_id': this.context.default_parent_id, + 'default_content_subtype': 'plain'} ); } + // return this._super(action, on_close); + }, + + /** Instantiate the composition form, with every parameters in context + or in the widget context. */ + instantiate_composition_form: function(context) { + if (this.compose_message_widget) { + this.compose_message_widget.destroy(); + } + this.compose_message_widget = new mail.ComposeMessage(this, { + 'context': _.extend(context || {}, this.context), + }); + var composition_node = this.$el.find('div.oe_mail_thread_action'); + composition_node.empty(); + var compose_done = this.compose_message_widget.appendTo(composition_node); + return compose_done; + }, + + refresh_composition_form: function (context) { + if (! this.compose_message_widget) return; + return this.compose_message_widget.refresh(context); + }, + + /** Clean the thread */ + message_clean: function() { + this.$el.find('div.oe_mail_thread_display').empty(); + }, + + /** Fetch messages + * @param {Bool} initial_mode: initial mode: try to use message_data or + * message_ids, if nothing available perform a message_read; otherwise + * directly perform a message_read + * @param {Array} additional_domain: added to this.domain + * @param {Object} additional_context: added to this.context + */ + message_fetch: function (initial_mode, additional_domain, additional_context) { + var self = this; + // domain and context: options + additional + fetch_domain = _.flatten([this.domain, additional_domain || []], true) + fetch_context = _.extend(this.context, additional_context || {}) + // initial mode: try to use message_data or message_ids + if (initial_mode && this.options.message_data) { + return this.message_display(this.options.message_data); + } + message_ids = initial_mode && this.options.message_ids != null && this.options.message_ids || false; + return this.ds_message.call('message_read', [message_ids, fetch_domain, this.options.thread_level, undefined, fetch_context] + ).then(this.proxy('message_display')); + }, + + /* Display a list of records + * A specific case is done for 'expandable' messages that are messages + displayed under a 'show more' button form + */ + message_display: function (records) { + var self = this; + var _expendable = false; _(records).each(function (record) { - var sub_msgs = []; - if ((record.parent_id == false || record.parent_id[0] == self.params.parent_id) && self.params.thread_level > 0 ) { - var sub_list = self.comments_structure['tree_struct'][record.id]['direct_childs']; - _(records).each(function (record) { - //if (record.parent_id == false || record.parent_id[0] == self.params.parent_id) return; - if (_.indexOf(sub_list, record.id) != -1) { - sub_msgs.push(record); - } - }); - self.display_comment(record); - self.thread = new mail.Thread(self, {'res_model': self.params.res_model, 'res_id': self.params.res_id, 'uid': self.params.uid, - 'records': sub_msgs, 'thread_level': (self.params.thread_level-1), 'parent_id': record.id, - 'is_wall': self.params.is_wall}); + if (record.type == 'expandable') { + _expendable = true; + self.update_fetch_more(true); + self.fetch_more_domain = record.domain; + self.fetch_more_context = record.context; + } + else { + self.display_record(record); + self.thread = new mail.Thread(self, self.domain, + { 'default_model': record.model, + 'default_res_id': record.res_id, + 'default_parent_id': record.id }, + { 'message_data': record.child_ids, + 'thread_level': self.options.thread_level - 1, + 'show_header_compose': false, + 'show_reply': self.options.show_reply && self.options.thread_level > 1, + 'show_reply_by_email': self.options.show_reply_by_email, + 'show_dd_hide': self.options.show_dd_hide, + 'show_dd_delete': self.options.show_dd_delete }); self.$el.find('li.oe_mail_thread_msg:last').append(''); self.thread.appendTo(self.$el.find('div.oe_mail_thread_subthread:last')); } - else if (self.params.thread_level == 0) { - self.display_comment(record); - } }); - mail.ChatterUtils.records_struct_update_after_display(this.comments_structure); - // update offset for "More" buttons - if (this.params.thread_level == 0) this.params.offset += records.length; + if (! _expendable) { + this.update_fetch_more(false); + } }, - /** Displays a record, performs text/link formatting */ - display_comment: function (record) { - record.body = mail.ChatterUtils.do_text_nl2br($.trim(record.body), true); - // if (record.type == 'email' && record.state == 'received') { - if (record.type == 'email') { - record.mini_url = ('/mail/static/src/img/email_icon.png'); - } else { - record.mini_url = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'image_small', record.user_id[0]); - } - // body text manipulation - if (record.subtype == 'plain') { - record.body = mail.ChatterUtils.do_text_remove_html_tags(record.body); - } - record.body = mail.ChatterUtils.do_replace_expressions(record.body); - // format date according to the user timezone + /** Displays a record and performs some formatting on the record : + * - record.date: formatting according to the user timezone + * - record.timerelative: relative time givein by timeago lib + * - record.avatar: image url + * - record.attachment_ids[].url: url of each attachment + * - record.is_author: is the current user the author of the record */ + display_record: function (record) { + // formatting and additional fields record.date = session.web.format_value(record.date, {type:"datetime"}); - // is the user the author ? - record.is_author = mail.ChatterUtils.is_author(this, record.user_id[0]); - // render - var rendered = session.web.qweb.render('mail.thread.message', {'record': record, 'thread': this, 'params': this.params, 'display': this.display}); - // expand feature + record.timerelative = $.timeago(record.date); + if (record.type == 'email') { + record.avatar = ('/mail/static/src/img/email_icon.png'); + } else { + record.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', record.author_id[0]); + } + for (var l in record.attachment_ids) { + var attach = record.attachment_ids[l]; + attach['url'] = mail.ChatterUtils.get_attachment_url(this.session, attach); + } + record.is_author = mail.ChatterUtils.is_author(this, record.author_user_id[0]); + // render, add the expand feature + var rendered = session.web.qweb.render('mail.thread.message', {'record': record, 'thread': this, 'options': this.options}); $(rendered).appendTo(this.$el.children('div.oe_mail_thread_display:first')); - this.$el.find('div.oe_mail_msg_record_body').expander({ - slicePoint: this.params.msg_more_limit, + this.$el.find('div.oe_mail_msg_body').expander({ + slicePoint: this.options.truncate_limit, expandText: 'read more', userCollapseText: '[^]', detailClass: 'oe_mail_msg_tail', @@ -769,54 +505,68 @@ openerp.mail = function(session) { }); }, - display_current_user: function () { - var avatar = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'image_small', this.params.uid); + /** Display 'show more' button */ + update_fetch_more: function (new_value) { + if (new_value) { + this.$el.find('div.oe_mail_thread_more:last').show(); + } else { + this.$el.find('div.oe_mail_thread_more:last').hide(); + } + }, + + display_user_avatar: function () { + var avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid); return this.$el.find('img.oe_mail_icon').attr('src', avatar); }, - do_comment: function () { - var comment_node = this.$el.find('textarea'); - var body_text = comment_node.val(); - comment_node.val(''); - return this.ds.call('message_append_note', [[this.params.res_id], '', body_text, this.params.parent_id, 'comment', 'plain']).then( - this.proxy('init_comments')); - }, - - /** - * Create a domain to fetch new comments according to - * comment already present in comments_structure - * @param {Object} comments_structure (see chatter utils) - * @returns {Array} fetch_domain (OpenERP domain style) - */ - get_fetch_domain: function (comments_structure) { - var domain = []; - var ids = comments_structure.root_ids.slice(); - var ids2 = []; - // must be child of current parent - if (this.params.parent_id) { domain.push(['id', 'child_of', this.params.parent_id]); } - _(comments_structure.root_ids).each(function (id) { // each record - ids.push(id); - ids2.push(id); - }); - if (this.params.parent_id != false) { - ids2.push(this.params.parent_id); + message_post: function (body) { + var self = this; + if (! body) { + var comment_node = this.$el.find('textarea'); + var body = comment_node.val(); + comment_node.val(''); } - // must not be children of already fetched messages - if (ids.length > 0) { - domain.push('&'); - domain.push('!'); - domain.push(['id', 'child_of', ids]); - } - if (ids2.length > 0) { - domain.push(['id', 'not in', ids2]); - } - return domain; + return this.ds_thread.call('message_post', [ + [this.context.default_res_id], body, false, 'comment', this.context.default_parent_id, undefined] + ).then(self.message_fetch()); }, - - do_more: function () { - domain = this.get_fetch_domain(this.comments_structure); - return this.fetch_comments(this.params.limit, this.params.offset, domain); + + /** Action: 'shows more' to fetch new messages */ + do_message_fetch_more: function () { + return this.message_fetch(false, this.fetch_more_domain, this.fetch_more_context); }, + + // TDE: keep currently because need something similar + // /** + // * Create a domain to fetch new comments according to + // * comment already present in comments_structure + // * @param {Object} comments_structure (see chatter utils) + // * @returns {Array} fetch_domain (OpenERP domain style) + // */ + // get_fetch_domain: function (comments_structure) { + // var domain = []; + // var ids = comments_structure.root_ids.slice(); + // var ids2 = []; + // // must be child of current parent + // if (this.options.parent_id) { domain.push(['id', 'child_of', this.options.parent_id]); } + // _(comments_structure.root_ids).each(function (id) { // each record + // ids.push(id); + // ids2.push(id); + // }); + // if (this.options.parent_id != false) { + // ids2.push(this.options.parent_id); + // } + // // must not be children of already fetched messages + // if (ids.length > 0) { + // domain.push('&'); + // domain.push('!'); + // domain.push(['id', 'child_of', ids]); + // } + // if (ids2.length > 0) { + // domain.push(['id', 'not in', ids2]); + // } + // return domain; + // }, }); @@ -825,178 +575,112 @@ openerp.mail = function(session) { * mail_thread Widget * ------------------------------------------------------------ * - * This widget handles the display of the Chatter on documents. + * This widget handles the display of messages on a document. Its main + * use is to receive a context and a domain, and to delegate the message + * fetching and displaying to the Thread widget. */ - - /* Add mail_thread widget to registry */ session.web.form.widgets.add('mail_thread', 'openerp.mail.RecordThread'); - - /** mail_thread widget: thread of comments */ mail.RecordThread = session.web.form.AbstractField.extend({ - // QWeb template to use when rendering the object template: 'mail.record_thread', - init: function() { + init: function() { this._super.apply(this, arguments); - this.params = this.options; - this.params.thread_level = this.params.thread_level || 0; - this.thread = null; - this.ds = new session.web.DataSet(this, this.view.model); - this.ds_users = new session.web.DataSet(this, 'res.users'); + this.options.domain = this.options.domain || []; + this.options.context = {'default_model': 'mail.thread', 'default_res_id': false}; + this.options.thread_level = this.options.thread_level || 0; }, start: function() { - // NB: all the widget should be modified to check the actual_mode property on view, not use - // any other method to know if the view is in create mode anymore + this._super.apply(this, arguments); + // NB: check the actual_mode property on view to know if the view is in create mode anymore this.view.on("change:actual_mode", this, this._check_visibility); this._check_visibility(); - mail.ChatterUtils.bind_events(this); - this.$el.find('button.oe_mail_button_followers').click(function () { self.do_toggle_followers(); }); - if (! this.params.see_subscribers_options) { - this.$el.find('button.oe_mail_button_followers').hide(); } - this.$el.find('button.oe_mail_button_follow').click(function () { self.do_follow(); }); - this.$el.find('button.oe_mail_button_unfollow').click(function () { self.do_unfollow(); }) - .mouseover(function () { $(this).html('Unfollow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); }) - .mouseleave(function () { $(this).html('Following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); }); }, - + _check_visibility: function() { this.$el.toggle(this.view.get("actual_mode") !== "create"); }, - - destroy: function () { - this._super.apply(this, arguments); - }, - + set_value: function() { + var self = this; this._super.apply(this, arguments); - if (! this.view.datarecord.id || - session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) { - this.$el.find('.oe_mail_thread').hide(); + if (! this.view.datarecord.id || session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) { + this.$el.find('oe_mail_thread').hide(); return; } + // update context + _.extend(this.options.context, { + default_res_id: this.view.datarecord.id, + default_model: this.view.model }); + // update domain + var domain = this.options.domain.concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]); // create and render Thread widget this.$el.find('div.oe_mail_recthread_main').empty(); - if (this.thread) this.thread.destroy(); - this.thread = new mail.Thread(this, {'res_model': this.view.model, 'res_id': this.view.datarecord.id, 'uid': this.session.uid, - 'thread_level': this.params.thread_level, 'show_post_comment': true, 'limit': 15}); - var thread_done = this.thread.appendTo(this.$el.find('div.oe_mail_recthread_main')); - return thread_done; + var thread = new mail.Thread(self, domain, this.options.context, + { 'thread_level': this.options.thread_level, + 'use_composer': true, + 'show_dd_delete': true, + 'show_reply_by_email': true }); + return thread.appendTo(this.$el.find('div.oe_mail_recthread_main')); }, }); /** * ------------------------------------------------------------ - * WallView Widget + * Wall Widget * ------------------------------------------------------------ * - * This widget handles the display of the Chatter on the Wall. + * This widget handles the display of messages on a Wall. Its main + * use is to receive a context and a domain, and to delegate the message + * fetching and displaying to the Thread widget. */ - - /* Add WallView widget to registry */ session.web.client_actions.add('mail.wall', 'session.mail.Wall'); - - /* WallView widget: a wall of messages */ mail.Wall = session.web.Widget.extend({ template: 'mail.wall', /** * @param {Object} parent parent - * @param {Object} [params] - * @param {Number} [params.limit=20] number of messages to show and fetch - * @param {Number} [params.search_view_id=false] search view id for messages - * @var {Array} comments_structure (see chatter utils) + * @param {Object} [options] + * @param {Array} [options.domain] domain on the Wall + * @param {Object} [options.context] context, is an object. It should + * contain default_model, default_res_id, to give it to the threads. + * @param {Number} [options.thread_level] number of thread levels to display + * 0 being flat. */ - init: function (parent, params) { + init: function (parent, options) { this._super(parent); - this.params = {}; - this.params.limit = params.limit || 25; - this.params.domain = params.domain || []; - this.params.context = params.context || {}; - this.params.res_model = params.res_model || false; - this.params.res_id = params.res_id || false; - this.params.search_view_id = params.search_view_id || false; - this.params.thread_level = params.thread_level || 1; - this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; - this.display_show_more = true; - this.thread_list = []; - this.search = {'domain': [], 'context': {}, 'groupby': {}} + this.options = options || {}; + this.options.domain = options.domain || []; + this.options.context = options.context || {}; + this.options.thread_level = options.thread_level || 1; this.search_results = {'domain': [], 'context': {}, 'groupby': {}} - // datasets - this.ds_msg = new session.web.DataSet(this, 'mail.message'); - this.ds_thread = new session.web.DataSet(this, 'mail.thread'); - this.ds_users = new session.web.DataSet(this, 'res.users'); + this.ds_msg = new session.web.DataSetSearch(this, 'mail.message'); }, start: function () { this._super.apply(this, arguments); - this.display_current_user(); - // add events - this.add_event_handlers(); - // load mail.message search view - var search_view_ready = this.load_search_view(this.params.search_view_id, {}, false); - // load composition form - var compose_done = this.instantiate_composition_form(); - // fetch first threads - var comments_ready = this.init_and_fetch_comments(this.params.limit, 0); - return (search_view_ready && comments_ready && compose_done); + var searchview_ready = this.load_searchview({}, false); + var thread_displayed = this.message_render(); + return (searchview_ready && thread_displayed); }, /** - * Override-hack of do_action: automatically reload the chatter. - * Normally it should be called only when clicking on 'Post/Send' - * in the composition form. */ - do_action: function(action, on_close) { - this.init_and_fetch_comments(); - if (this.compose_message_widget) { - this.compose_message_widget.reinit(); } - return this._super(action, on_close); - }, - - destroy: function () { - this._super.apply(this, arguments); - }, - - instantiate_composition_form: function(mode, msg_id) { - if (this.compose_message_widget) { - this.compose_message_widget.destroy(); - } - this.compose_message_widget = new mail.ComposeMessage(this, { - 'extended_mode': false, 'uid': this.session.uid, 'res_model': this.params.res_model, - 'res_id': this.params.res_id, 'mode': mode || 'comment', 'msg_id': msg_id }); - var composition_node = this.$el.find('div.oe_mail_wall_action'); - composition_node.empty(); - var compose_done = this.compose_message_widget.appendTo(composition_node); - return compose_done; - }, - - /** Add events */ - add_event_handlers: function () { - var self = this; - // display more threads - this.$el.find('button.oe_mail_wall_button_more').click(function () { return self.do_more(); }); - }, - - /** - * Loads the mail.message search view - * @param {Number} view_id id of the search view to load + * Load the mail.message search view * @param {Object} defaults ?? * @param {Boolean} hidden some kind of trick we do not care here */ - load_search_view: function (view_id, defaults, hidden) { + load_searchview: function (defaults, hidden) { var self = this; - this.searchview = new session.web.SearchView(this, this.ds_msg, view_id || false, defaults || {}, hidden || false); - var search_view_loaded = this.searchview.appendTo(this.$el.find('.oe_view_manager_view_search')); - return $.when(search_view_loaded).then(function () { + this.searchview = new session.web.SearchView(this, this.ds_msg, false, defaults || {}, hidden || false); + return this.searchview.appendTo(this.$el.find('.oe_view_manager_view_search')).then(function () { self.searchview.on_search.add(self.do_searchview_search); }); }, /** - * Aggregate the domains, contexts and groupbys in parameter - * with those from search form, and then calls fetch_comments - * to actually fetch comments + * Get the domains, contexts and groupbys in parameter from search + * view, then render the filtered threads. * @param {Array} domains * @param {Array} contexts * @param {Array} groupbys @@ -1011,100 +695,24 @@ openerp.mail = function(session) { self.search_results['context'] = results.context; self.search_results['domain'] = results.domain; self.search_results['groupby'] = results.group_by; - return self.init_and_fetch_comments(); + return self.message_render(); }); }, - display_current_user: function () { - //return this.$el.find('img.oe_mail_msg_image').attr('src', this.thread_get_avatar('res.users', 'avatar', this.session.uid)); - }, - - /** - * Initializes the wall and calls fetch_comments - * @param {Number} limit: number of notifications to fetch - * @param {Number} offset: offset in notifications search - * @param {Array} domain - * @param {Array} context - */ - init_and_fetch_comments: function() { - this.search['domain'] = _.union(this.params.domain, this.search_results.domain); - this.search['context'] = _.extend(this.params.context, this.search_results.context); - this.display_show_more = true; - this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; + /** Clean and display the threads */ + message_render: function () { this.$el.find('ul.oe_mail_wall_threads').empty(); - return this.fetch_comments(this.params.limit, 0); - }, - - /** - * Fetches wall messages - * @param {Number} limit: number of notifications to fetch - * @param {Number} offset: offset in notifications search - * @param {Array} domain - * @param {Array} context - */ - fetch_comments: function (limit, offset, additional_domain, additional_context) { - var self = this; - if (additional_domain) var fetch_domain = this.search['domain'].concat(additional_domain); - else var fetch_domain = this.search['domain']; - if (additional_context) var fetch_context = _.extend(this.search['context'], additional_context); - else var fetch_context = this.search['context']; - return this.ds_thread.call('message_get_pushed_messages', - [[this.session.uid], true, [], (limit || 0), (offset || 0), fetch_domain, fetch_context]).then(this.proxy('display_comments')); - }, - - /** - * @param {Array} records records to show in threads - */ - display_comments: function (records) { - var self = this; - this.do_update_show_more(records.length >= self.params.limit); - mail.ChatterUtils.records_struct_add_records(this.comments_structure, records, false); - _(this.comments_structure['new_root_ids']).each(function (root_id) { - var records = self.comments_structure.tree_struct[root_id]['for_thread_msgs']; - var model_name = self.comments_structure.msgs[root_id]['model']; - var res_id = self.comments_structure.msgs[root_id]['res_id']; - var render_res = session.web.qweb.render('mail.wall_thread_container', {}); - $('
|
@@ -26,19 +27,9 @@
-
| ||
Please call me as soon as possible this afternoon!
- +--
Sylvie
@@ -62,51 +63,547 @@ Content-Transfer-Encoding: quoted-printable
------=_Part_4200734_24778174.1344608186754--
"""
+MAIL_TEMPLATE_PLAINTEXT = """Return-Path: \nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n
\n',
+ 'plaintext mail incorrectly parsed')
+
+ def test_10_many2many_reference_field(self):
+ """ Tests designed for the many2many_reference field (follower_ids).
+ We will test to perform writes using the many2many commands 0, 3, 4,
+ 5 and 6. """
+ cr, uid = self.cr, self.uid
+ user_admin = self.res_users.browse(cr, uid, uid)
+ group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
+
+ # Create partner Bert Poilu
+ partner_bert_id = self.res_partner.create(cr, uid, {'name': 'Bert Poilu'})
+
+ # Create 'disturbing' values in mail.followers: same res_id, other res_model; same res_model, other res_id
+ group_dummy_id = self.mail_group.create(cr, uid,
+ {'name': 'Dummy group'})
+ self.mail_followers.create(cr, uid,
+ {'res_model': 'mail.thread', 'res_id': self.group_pigs_id, 'partner_id': partner_bert_id})
+ self.mail_followers.create(cr, uid,
+ {'res_model': 'mail.group', 'res_id': group_dummy_id, 'partner_id': partner_bert_id})
+
+ # Pigs just created: should be only Admin as follower
+ follower_ids = set([follower.id for follower in group_pigs.message_follower_ids])
+ self.assertEqual(follower_ids, set([user_admin.partner_id.id]), 'Admin should be the only Pigs fan')
+
+ # Subscribe Bert through a '4' command
+ group_pigs.write({'message_follower_ids': [(4, partner_bert_id)]})
+ group_pigs.refresh()
+ follower_ids = set([follower.id for follower in group_pigs.message_follower_ids])
+ self.assertEqual(follower_ids, set([partner_bert_id, user_admin.partner_id.id]), 'Bert and Admin should be the only Pigs fans')
+
+ # Unsubscribe Bert through a '3' command
+ group_pigs.write({'message_follower_ids': [(3, partner_bert_id)]})
+ group_pigs.refresh()
+ follower_ids = set([follower.id for follower in group_pigs.message_follower_ids])
+ self.assertEqual(follower_ids, set([user_admin.partner_id.id]), 'Admin should be the only Pigs fan')
+
+ # Set followers through a '6' command
+ group_pigs.write({'message_follower_ids': [(6, 0, [partner_bert_id])]})
+ group_pigs.refresh()
+ follower_ids = set([follower.id for follower in group_pigs.message_follower_ids])
+ self.assertEqual(follower_ids, set([partner_bert_id]), 'Bert should be the only Pigs fan')
+
+ # Add a follower created on the fly through a '0' command
+ group_pigs.write({'message_follower_ids': [(0, 0, {'name': 'Patrick Fiori'})]})
+ partner_patrick_id = self.res_partner.search(cr, uid, [('name', '=', 'Patrick Fiori')])[0]
+ group_pigs.refresh()
+ follower_ids = set([follower.id for follower in group_pigs.message_follower_ids])
+ self.assertEqual(follower_ids, set([partner_bert_id, partner_patrick_id]), 'Bert and Patrick should be the only Pigs fans')
+
+ # Finally, unlink through a '5' command
+ group_pigs.write({'message_follower_ids': [(5, 0)]})
+ group_pigs.refresh()
+ follower_ids = set([follower.id for follower in group_pigs.message_follower_ids])
+ self.assertFalse(follower_ids, 'Pigs group should not have fans anymore')
+
+ # Test dummy data has not been altered
+ fol_obj_ids = self.mail_followers.search(cr, uid, [('res_model', '=', 'mail.thread'), ('res_id', '=', self.group_pigs_id)])
+ follower_ids = set([follower.partner_id.id for follower in self.mail_followers.browse(cr, uid, fol_obj_ids)])
+ self.assertEqual(follower_ids, set([partner_bert_id]), 'Bert should be the follower of dummy mail.thread data')
+ fol_obj_ids = self.mail_followers.search(cr, uid, [('res_model', '=', 'mail.group'), ('res_id', '=', group_dummy_id)])
+ follower_ids = set([follower.partner_id.id for follower in self.mail_followers.browse(cr, uid, fol_obj_ids)])
+ self.assertEqual(follower_ids, set([partner_bert_id, user_admin.partner_id.id]), 'Bert and Admin should be the followers of dummy mail.group data')
+
+ def test_11_message_followers(self):
+ """ Tests designed for the subscriber API. """
+ cr, uid = self.cr, self.uid
+ user_admin = self.res_users.browse(cr, uid, uid)
+ group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
+
+ # Create user Raoul
+ user_raoul_id = self.res_users.create(cr, uid, {'name': 'Raoul Grosbedon', 'login': 'raoul'})
+ user_raoul = self.res_users.browse(cr, uid, user_raoul_id)
+
+ # Subscribe Raoul three times (niak niak) through message_subscribe_users
+ group_pigs.message_subscribe_users([user_raoul_id, user_raoul_id])
+ group_pigs.message_subscribe_users([user_raoul_id])
+ group_pigs.refresh()
+ follower_ids = [follower.id for follower in group_pigs.message_follower_ids]
+ self.assertEqual(len(follower_ids), 2, 'There should be 2 Pigs fans')
+ self.assertEqual(set(follower_ids), set([user_raoul.partner_id.id, user_admin.partner_id.id]), 'Admin and Raoul should be the only 2 Pigs fans')
+
+ # Unsubscribe Raoul twice through message_unsubscribe_users
+ group_pigs.message_unsubscribe_users([user_raoul_id, user_raoul_id])
+ group_pigs.refresh()
+ follower_ids = [follower.id for follower in group_pigs.message_follower_ids]
+ self.assertEqual(follower_ids, [user_admin.partner_id.id], 'Admin must be the only Pigs fan')
+
+ def test_20_message_post(self):
+ """ Tests designed for message_post. """
+ cr, uid = self.cr, self.uid
+ self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'})
+ user_admin = self.res_users.browse(cr, uid, uid)
+ group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
+
+ # 0 - Admin
+ p_a_id = user_admin.partner_id.id
+ # 1 - Bert Tartopoils, with email, should receive emails for comments and emails
+ p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'})
+ # 2 - Carine Poilvache, with email, should never receive emails
+ p_c_id = self.res_partner.create(cr, uid, {'name': 'Carine Poilvache', 'email': 'c@c', 'notification_email_send': 'email'})
+ # 3 - Dédé Grosbedon, without email, to test email verification; should receive emails for every message
+ p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'notification_email_send': 'all'})
+
+ # Subscribe #1, #2
+ group_pigs.message_subscribe([p_b_id, p_c_id])
+
+ # Mail data
+ _subject = 'Pigs'
+ _mail_subject = '%s posted on %s' % (user_admin.name, group_pigs.name)
+ _body1 = 'Pigs rules'
+ _mail_body1 = 'Pigs rules\nAdmin
\n'
+ _mail_bodyalt1 = 'Pigs rules\nAdmin'
+ _body2 = 'Pigs rules'
+ _mail_body2 = html_sanitize('Pigs rules\nAdmin
\n')
+ _mail_bodyalt2 = 'Pigs rules\nAdmin'
+ _attachments = [('First', 'My first attachment'), ('Second', 'My second attachment')]
+
+ # CASE1: post comment, body and subject specified
+ msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body1, subject=_subject, type='comment')
+ message = self.mail_message.browse(cr, uid, msg_id)
+ sent_email = self._build_email_kwargs
+ # Test: notifications have been deleted
+ self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg_id)]), 'mail.mail notifications should have been auto-deleted!')
+ # Test: mail_message: subject is _subject, body is _body1 (no formatting done)
+ self.assertEqual(message.subject, _subject, 'mail.message subject incorrect')
+ self.assertEqual(message.body, _body1, 'mail.message body incorrect')
+ # Test: sent_email: email send by server: correct subject, body; body_alternative
+ self.assertEqual(sent_email['subject'], _subject, 'sent_email subject incorrect')
+ self.assertEqual(sent_email['body'], _mail_body1, 'sent_email body incorrect')
+ self.assertEqual(sent_email['body_alternative'], _mail_bodyalt1, 'sent_email body_alternative is incorrect')
+ # Test: mail_message: partner_ids = group followers
+ message_pids = set([partner.id for partner in message.partner_ids])
+ test_pids = set([p_a_id, p_b_id, p_c_id])
+ self.assertEqual(test_pids, message_pids, 'mail.message partners incorrect')
+ # Test: notification linked to this message = group followers = partner_ids
+ notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)])
+ notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)])
+ self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect')
+ # Test: sent_email: email_to should contain b@b, not c@c (pref email), not a@a (writer)
+ self.assertEqual(sent_email['email_to'], ['b@b'], 'sent_email email_to is incorrect')
+
+ # CASE2: post an email with attachments, parent_id, partner_ids
+ # TESTS: automatic subject, signature in body_html, attachments propagation
+ msg_id2 = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body2, type='email',
+ partner_ids=[(6, 0, [p_d_id])], parent_id=msg_id, attachments=_attachments)
+ message = self.mail_message.browse(cr, uid, msg_id2)
+ sent_email = self._build_email_kwargs
+ self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg_id2)]), 'mail.mail notifications should have been auto-deleted!')
+
+ # Test: mail_message: subject is False, body is _body2 (no formatting done), parent_id is msg_id
+ self.assertEqual(message.subject, False, 'mail.message subject incorrect')
+ self.assertEqual(message.body, html_sanitize(_body2), 'mail.message body incorrect')
+ self.assertEqual(message.parent_id.id, msg_id, 'mail.message parent_id incorrect')
+ # Test: sent_email: email send by server: correct subject, body, body_alternative
+ self.assertEqual(sent_email['subject'], _mail_subject, 'sent_email subject incorrect')
+ self.assertEqual(sent_email['body'], _mail_body2, 'sent_email body incorrect')
+ self.assertEqual(sent_email['body_alternative'], _mail_bodyalt2, 'sent_email body_alternative incorrect')
+ # Test: mail_message: partner_ids = group followers
+ message_pids = set([partner.id for partner in message.partner_ids])
+ test_pids = set([p_a_id, p_b_id, p_c_id, p_d_id])
+ self.assertEqual(message_pids, test_pids, 'mail.message partners incorrect')
+ # Test: notifications linked to this message = group followers = partner_ids
+ notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)])
+ notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)])
+ self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect')
+ # Test: sent_email: email_to should contain b@b, c@c, not a@a (writer)
+ self.assertEqual(set(sent_email['email_to']), set(['b@b', 'c@c']), 'sent_email email_to incorrect')
+ # Test: attachments
+ for attach in message.attachment_ids:
+ self.assertEqual(attach.res_model, 'mail.group', 'mail.message attachment res_model incorrect')
+ self.assertEqual(attach.res_id, self.group_pigs_id, 'mail.message attachment res_id incorrect')
+ self.assertIn((attach.name, attach.datas.decode('base64')), _attachments,
+ 'mail.message attachment name / data incorrect')
+
+ def test_21_message_compose_wizard(self):
+ """ Tests designed for the mail.compose.message wizard. """
+ cr, uid = self.cr, self.uid
+ mail_compose = self.registry('mail.compose.message')
+ self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'})
+ user_admin = self.res_users.browse(cr, uid, uid)
+ group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
+ group_bird_id = self.mail_group.create(cr, uid, {'name': 'Bird', 'description': 'Bird resistance'})
+ group_bird = self.mail_group.browse(cr, uid, group_bird_id)
+
+ # Mail data
+ _subject = 'Pigs'
+ _body_text = 'Pigs rules'
+ _msg_reply = 'Re: Pigs'
+ _msg_body = 'Pigs rules
'
+ _attachments = [
+ {'name': 'First', 'datas_fname': 'first.txt', 'datas': 'My first attachment'.encode('base64')},
+ {'name': 'Second', 'datas_fname': 'second.txt', 'datas': 'My second attachment'.encode('base64')}
+ ]
+ _attachments_test = [('first.txt', 'My first attachment'), ('second.txt', 'My second attachment')]
+
+ # Create partners
+ # 0 - Admin
+ p_a_id = user_admin.partner_id.id
+ # 1 - Bert Tartopoils, with email, should receive emails for comments and emails
+ p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'})
+ # 2 - Carine Poilvache, with email, should never receive emails
+ p_c_id = self.res_partner.create(cr, uid, {'name': 'Carine Poilvache', 'email': 'c@c', 'notification_email_send': 'email'})
+ # 3 - Dédé Grosbedon, without email, to test email verification; should receive emails for every message
+ p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'notification_email_send': 'all'})
+
+ # Subscribe #1
+ group_pigs.message_subscribe([p_b_id])
+
+ # ----------------------------------------
+ # CASE1: comment on group_pigs
+ # ----------------------------------------
+
+ # 1. Comment group_pigs with body_text and subject
+ compose_id = mail_compose.create(cr, uid,
+ {'subject': _subject, 'body_text': _body_text, 'partner_ids': [(4, p_c_id), (4, p_d_id)]},
+ {'default_composition_mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_pigs_id})
+ compose = mail_compose.browse(cr, uid, compose_id)
+ # Test: mail.compose.message: composition_mode, model, res_id
+ self.assertEqual(compose.composition_mode, 'comment', 'mail.compose.message incorrect composition_mode')
+ self.assertEqual(compose.model, 'mail.group', 'mail.compose.message incorrect model')
+ self.assertEqual(compose.res_id, self.group_pigs_id, 'mail.compose.message incorrect res_id')
+
+ # 2. Post the comment, get created message
+ mail_compose.send_mail(cr, uid, [compose_id])
+ group_pigs.refresh()
+ message = group_pigs.message_ids[0]
+ # Test: mail.message: subject, body inside pre
+ self.assertEqual(message.subject, False, 'mail.message incorrect subject')
+ self.assertEqual(message.body, _msg_body, 'mail.message incorrect body')
+ # Test: mail.message: partner_ids = entries in mail.notification: group_pigs fans (a, b) + mail.compose.message partner_ids (c, d)
+ msg_pids = [partner.id for partner in message.partner_ids]
+ test_pids = [p_a_id, p_b_id, p_c_id, p_d_id]
+ notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)])
+ self.assertEqual(len(notif_ids), 4, 'mail.message: too much notifications created')
+ self.assertEqual(set(msg_pids), set(test_pids), 'mail.message partner_ids incorrect')
+
+ # ----------------------------------------
+ # CASE2: reply to last comment with attachments
+ # ----------------------------------------
+
+ # 1. Update last comment subject, reply with attachments
+ message.write({'subject': _subject})
+ compose_id = mail_compose.create(cr, uid,
+ {'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])]},
+ {'default_composition_mode': 'reply', 'default_model': 'mail.thread', 'default_res_id': self.group_pigs_id, 'default_parent_id': message.id})
+ compose = mail_compose.browse(cr, uid, compose_id)
+ # Test: model, res_id, parent_id, content_subtype
+ self.assertEqual(compose.model, 'mail.group', 'mail.compose.message incorrect model')
+ self.assertEqual(compose.res_id, self.group_pigs_id, 'mail.compose.message incorrect res_id')
+ self.assertEqual(compose.parent_id.id, message.id, 'mail.compose.message incorrect parent_id')
+ self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message incorrect content_subtype')
+
+ # 2. Post the comment, get created message
+ parent_id = message.id
+ mail_compose.send_mail(cr, uid, [compose_id])
+ group_pigs.refresh()
+ message = group_pigs.message_ids[0]
+ # Test: mail.message: subject as Re:.., body in html, parent_id
+ self.assertEqual(message.subject, _msg_reply, 'mail.message incorrect subject')
+ self.assertIn('Administrator wrote:Pigs rules
%s' % (tools.ustr(body_html)), - reply_body_text = '\n%s%s\n%s\n%s' % (sent_date, sender, quoted_body_text, current_user.signature) - reply_body_html = '
%(body)s
%s' % tools.ustr(wizard.body_text), + 'parent_id': wizard.parent_id and wizard.parent_id.id, + 'partner_ids': [(4, partner.id) for partner in wizard.partner_ids], + 'attachments': [(attach.datas_fname or attach.name, base64.b64decode(attach.datas)) for attach in wizard.attachment_ids], + } + # mass mailing: render and override default values + if mass_mail_mode and wizard.model: + email_dict = self.render_message(cr, uid, wizard, res_id, context=context) + new_partner_ids = email_dict.pop('partner_ids', []) + post_values['partner_ids'] += [(4, partner_id) for partner_id in new_partner_ids] + new_attachments = email_dict.pop('attachments', []) + post_values['attachments'] += new_attachments + post_values.update(email_dict) + # post the message + active_model_pool.message_post(cr, uid, [res_id], type='comment', context=context, **post_values) + # post process: update attachments, because id is not necessarily known when adding attachments in Chatter + self.pool.get('ir.attachment').write(cr, uid, [attach.id for attach in wizard.attachment_ids], {'res_id': wizard.id}, context=context) return {'type': 'ir.actions.act_window_close'} + def render_message(self, cr, uid, wizard, res_id, context=None): + """ Generate an email from the template for given (wizard.model, res_id) + pair. This method is meant to be inherited by email_template that + will produce a more complete dictionary. """ + return { + 'subject': self.render_template(cr, uid, wizard.subject, wizard.model, res_id, context), + 'body': self.render_template(cr, uid, wizard.body, wizard.model, res_id, context), + } + def render_template(self, cr, uid, template, model, res_id, context=None): - """Render the given template text, replace mako-like expressions ``${expr}`` - with the result of evaluating these expressions with an evaluation context - containing: + """ Render the given template text, replace mako-like expressions ``${expr}`` + with the result of evaluating these expressions with an evaluation context + containing: * ``user``: browse_record of the current user * ``object``: browse_record of the document record this mail is related to * ``context``: the context passed to the mail composition wizard - :param str template: the template text to render - :param str model: model name of the document record this mail is related to. - :param int res_id: id of the document record this mail is related to. + :param str template: the template text to render + :param str model: model name of the document record this mail is related to. + :param int res_id: id of the document record this mail is related to. """ if context is None: context = {} + def merge(match): exp = str(match.group()[2:-1]).strip() - result = eval(exp, - { - 'user' : self.pool.get('res.users').browse(cr, uid, uid, context=context), - 'object' : self.pool.get(model).browse(cr, uid, res_id, context=context), - 'context': dict(context), # copy context to prevent side-effects of eval - }) - if result in (None, False): - return "" - return tools.ustr(result) + result = eval(exp, { + 'user': self.pool.get('res.users').browse(cr, uid, uid, context=context), + 'object': self.pool.get(model).browse(cr, uid, res_id, context=context), + 'context': dict(context), # copy context to prevent side-effects of eval + }) + return result and tools.ustr(result) or '' return template and EXPRESSION_PATTERN.sub(merge, template) - - -class mail_compose_message_extended(osv.TransientModel): - """ Extension of 'mail.compose.message' to support default field values related - to CRM-like models that follow the following conventions: - - 1. The model object must have an attribute '_mail_compose_message' equal to True. - - 2. The model should define the following fields: - - 'name' as subject of the message (required); - - 'email_from' as destination email address (required); - - 'email_cc' as cc email addresses (required); - - 'section_id.reply_to' as reply-to address (optional). - """ - _inherit = 'mail.compose.message' - - def get_value(self, cr, uid, model, res_id, context=None): - """ Overrides the default implementation to provide more default field values - related to the corresponding CRM case. - """ - result = super(mail_compose_message_extended, self).get_value(cr, uid, model, res_id, context=context) - model_obj = self.pool.get(model) - if getattr(model_obj, '_mail_compose_message', False) and res_id: - data = model_obj.browse(cr, uid , res_id, context) - result.update({ - 'email_to': data.email_from or False, - 'email_cc': tools.ustr(data.email_cc or ''), - 'subject': data.name or False, - }) - if hasattr(data, 'section_id'): - result['reply_to'] = data.section_id and data.section_id.reply_to or False - return result - - def onchange_email_mode(self, cr, uid, ids, value, model, res_id, context=None): - """ Overrides the default implementation to provide default values for - dest_partner_ids. This method checks that a partner maching the - ``email_from`` of the record exists. It it does not exist, it - creates a new partner. The found or created partner is then added - in dest_partner_ids. - Partner check/creation valid inly if the value is True, and if - the model has the ``_mail_compose_message`` attribute. - """ - result = super(mail_compose_message_extended, self).onchange_email_mode(cr, uid, ids, value, model, res_id, context=context) - model_obj = self.pool.get(model) - if not value or not (getattr(model_obj, '_mail_compose_message', False) and res_id): - return result - data = model_obj.browse(cr, uid , res_id, context=context) - partner_obj = self.pool.get('res.partner') - partner_ids = partner_obj.search(cr, uid, [('email', '=', data.email_from)], context=context) - if partner_ids: - partner_id = partner_ids[0] - else: - partner_id = partner_obj.name_create(cr, uid, data.email_from, context=context)[0] - result['value'].update({ - 'dest_partner_ids': [partner_id], - 'email_cc': tools.ustr(data.email_cc or ''), - }) - if hasattr(data, 'section_id'): - result['value']['reply_to'] = data.section_id and data.section_id.reply_to or False - return result - - def onchange_formatting(self, cr, uid, ids, value, model, res_id, context=None): - """ Overrides the default implementation to provide default values for - the subject. - Subject re-creation valid only if the value is True, and if the - model has the ``_mail_compose_message`` attribute. - """ - result = super(mail_compose_message_extended, self).onchange_formatting(cr, uid, ids, value, model, res_id, context=context) - model_obj = self.pool.get(model) - if not value or not (getattr(model_obj, '_mail_compose_message', False) and res_id): - return result - data = model_obj.browse(cr, uid , res_id, context=context) - result['value'].update({ - 'subject': data.name or False, - }) - return result - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml index 73fb03cdc28..2899c0c9a41 100644 --- a/addons/mail/wizard/mail_compose_message_view.xml +++ b/addons/mail/wizard/mail_compose_message_view.xml @@ -6,73 +6,87 @@
Thanks for the genuine interest you have shown in OpenERP.
+If any further information is required, do not hesitate to reply to this message.
+Regards,OpenERP Team,
]]>We have very good offer that might suit you. + We suggest you subscribe to the OpenERP Discovery Day on May 2010.
+If any further information is required, do not hesitate to reply to this message.
+Regards,OpenERP Team,
]]>Thanks for showing interest and for subscribing to the OpenERP Discovery Day.
+If any further information is required, do not hesitate to reply to this message.
+Regards,OpenERP Team,
]]>Thanks for showing interest and buying the OpenERP book.
If any further information required kindly revert back. - I really appreciate your co-operation on this. - Regards,OpenERP Team,Regards,OpenERP Team,
]]>We have very good offer that might suit you. + For our gold partners,We are arranging free technical training on june,2010.
+If any further information is required, do not hesitate to reply to this message.
+Regards,OpenERP Team,
]]>We have very good offer that might suit you. + For our silver partners,We are paid technical training on june,2010.
+If any further information is required, do not hesitate to reply to this message.
+Regards,OpenERP Team,
]]>We have very good offer that might suit you. + For our silver partners, we are offering Gold partnership.
+If any further information is required, do not hesitate to reply to this message.
+Regards,OpenERP Team,
]]>Thanks for showing interest and for subscribing to technical training.
+ If any further information required kindly revert back.I really appreciate your co-operation on this. +If any further information is required, do not hesitate to reply to this message.
+Regards,OpenERP Team,
]]>+ When processing a sale order for this product, the delivery order + will contain the raw materials, instead of the finished product. +
+
Click to add a component to a bill of material.
@@ -520,6 +525,7 @@
action="mrp_bom_form_action2"
id="menu_mrp_bom_form_action2"
parent="menu_mrp_configuration"
+ groups="base.group_no_one"
sequence="20"/>
You can define another list of available currencies on the
Cash Registers tab of the
+ Enter a reference for this product, it will be converted
+ automatically to a valid EAN number.
+
- You will first have to control your cash amount in your cash register.
- Then you will be able to open the session and start selling with the touchscreen
- interface.
+ You may have to control your cash amount in your cash register, before
+ being able to start selling through the touchscreen interface.
+
+ Click to continue the session.
+
+ The session
+ Notes:
+ Last modified by: N/A
+
+
+
+
+
+ Fill is this for to launch a procurement request for this
+ product. According to the product configuration, this may
+ trigger a draft purchase order, a manufacturing order or
+ a new task.
+
-
-
-
-
-
+ Manufacturing Order
- OpenERP Entreprise customers may safely use pad.openerp.com
+ You must configure the etherpad through the menu Setting > Companies > Companies, in the configuration tab of your company.
\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2012-09-08 04:54+0000\n"
+"X-Generator: Launchpad (build 15914)\n"
+
+#. module: pad_project
+#: constraint:project.task:0
+msgid "Error ! Task end-date must be greater then task start-date"
+msgstr "Hata! görev bitiş tarihi başlangıç tarihinden sonra olmalı"
+
+#. module: pad_project
+#: model:ir.model,name:pad_project.model_project_task
+msgid "Task"
+msgstr "Görev"
+
+#. module: pad_project
+#: view:project.task:0
+msgid "Pad"
+msgstr ""
+
+#. module: pad_project
+#: constraint:project.task:0
+msgid "Error ! You cannot create recursive tasks."
+msgstr "Hata ! Yinelenen görevler oluşturamazsınız."
diff --git a/addons/plugin/i18n/nb.po b/addons/plugin/i18n/nb.po
new file mode 100644
index 00000000000..0ef79314ec0
--- /dev/null
+++ b/addons/plugin/i18n/nb.po
@@ -0,0 +1,23 @@
+# Norwegian Bokmal translation for openobject-addons
+# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
+# This file is distributed under the same license as the openobject-addons package.
+# FIRST AUTHOR
+ You can continue sales from the touchscreen interface by clicking on "Start Selling" or close the cash register session.
%s
' % body}, context=context)
return {'type': 'ir.actions.act_window_close'}
diff --git a/addons/portal_event/event_view.xml b/addons/portal_event/event_view.xml
index 1dd9fee7181..675bddbc42e 100644
--- a/addons/portal_event/event_view.xml
+++ b/addons/portal_event/event_view.xml
@@ -3,7 +3,7 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- Code:
+
+
+
+
+
+
-
+
-
- Deadline
-
-
-
- Progress
-
-