diff --git a/addons/account/project/project_view.xml b/addons/account/project/project_view.xml index 70db95425b1..342663e700f 100644 --- a/addons/account/project/project_view.xml +++ b/addons/account/project/project_view.xml @@ -160,7 +160,7 @@ account.analytic.line.tree account.analytic.line - + diff --git a/addons/anonymization/i18n/mn.po b/addons/anonymization/i18n/mn.po new file mode 100644 index 00000000000..cd54ad8ef6c --- /dev/null +++ b/addons/anonymization/i18n/mn.po @@ -0,0 +1,213 @@ +# Mongolian 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 , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-02-08 00:35+0000\n" +"PO-Revision-Date: 2012-10-22 02:35+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Mongolian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-10-22 04:40+0000\n" +"X-Generator: Launchpad (build 16165)\n" + +#. module: anonymization +#: model:ir.model,name:anonymization.model_ir_model_fields_anonymize_wizard +msgid "ir.model.fields.anonymize.wizard" +msgstr "" + +#. module: anonymization +#: field:ir.model.fields.anonymization,field_name:0 +msgid "Field Name" +msgstr "" + +#. module: anonymization +#: field:ir.model.fields.anonymization,field_id:0 +msgid "Field" +msgstr "" + +#. module: anonymization +#: field:ir.model.fields.anonymization.history,state:0 +#: field:ir.model.fields.anonymize.wizard,state:0 +msgid "State" +msgstr "" + +#. module: anonymization +#: field:ir.model.fields.anonymize.wizard,file_import:0 +msgid "Import" +msgstr "" + +#. module: anonymization +#: model:ir.model,name:anonymization.model_ir_model_fields_anonymization +msgid "ir.model.fields.anonymization" +msgstr "" + +#. module: anonymization +#: field:ir.model.fields.anonymization.history,direction:0 +msgid "Direction" +msgstr "" + +#. module: anonymization +#: model:ir.actions.act_window,name:anonymization.action_ir_model_fields_anonymization_tree +#: view:ir.model.fields.anonymization:0 +#: model:ir.ui.menu,name:anonymization.menu_administration_anonymization_fields +msgid "Anonymized Fields" +msgstr "" + +#. module: anonymization +#: model:ir.ui.menu,name:anonymization.menu_administration_anonymization +msgid "Database anonymization" +msgstr "" + +#. module: anonymization +#: selection:ir.model.fields.anonymization.history,direction:0 +msgid "clear -> anonymized" +msgstr "" + +#. module: anonymization +#: selection:ir.model.fields.anonymization,state:0 +#: selection:ir.model.fields.anonymize.wizard,state:0 +msgid "Anonymized" +msgstr "" + +#. module: anonymization +#: field:ir.model.fields.anonymization,state:0 +msgid "unknown" +msgstr "" + +#. module: anonymization +#: field:ir.model.fields.anonymization,model_id:0 +msgid "Object" +msgstr "" + +#. module: anonymization +#: field:ir.model.fields.anonymization.history,filepath:0 +msgid "File path" +msgstr "" + +#. module: anonymization +#: field:ir.model.fields.anonymization.history,date:0 +msgid "Date" +msgstr "" + +#. module: anonymization +#: field:ir.model.fields.anonymize.wizard,file_export:0 +msgid "Export" +msgstr "" + +#. module: anonymization +#: view:ir.model.fields.anonymize.wizard:0 +msgid "Reverse the Database Anonymization" +msgstr "" + +#. module: anonymization +#: view:ir.model.fields.anonymize.wizard:0 +msgid "Database Anonymization" +msgstr "" + +#. module: anonymization +#: model:ir.ui.menu,name:anonymization.menu_administration_anonymization_wizard +msgid "Anonymize database" +msgstr "" + +#. module: anonymization +#: view:ir.model.fields.anonymization.history:0 +#: field:ir.model.fields.anonymization.history,field_ids:0 +msgid "Fields" +msgstr "" + +#. module: anonymization +#: selection:ir.model.fields.anonymization,state:0 +#: selection:ir.model.fields.anonymize.wizard,state:0 +msgid "Clear" +msgstr "" + +#. module: anonymization +#: view:ir.model.fields.anonymize.wizard:0 +#: field:ir.model.fields.anonymize.wizard,summary:0 +msgid "Summary" +msgstr "" + +#. module: anonymization +#: view:ir.model.fields.anonymization:0 +msgid "Anonymized Field" +msgstr "" + +#. module: anonymization +#: selection:ir.model.fields.anonymize.wizard,state:0 +msgid "Unstable" +msgstr "Тогтмол биш" + +#. module: anonymization +#: selection:ir.model.fields.anonymization.history,state:0 +msgid "Exception occured" +msgstr "" + +#. module: anonymization +#: selection:ir.model.fields.anonymization,state:0 +#: selection:ir.model.fields.anonymize.wizard,state:0 +msgid "Not Existing" +msgstr "" + +#. module: anonymization +#: field:ir.model.fields.anonymization,model_name:0 +msgid "Object Name" +msgstr "" + +#. module: anonymization +#: model:ir.actions.act_window,name:anonymization.action_ir_model_fields_anonymization_history_tree +#: view:ir.model.fields.anonymization.history:0 +#: model:ir.ui.menu,name:anonymization.menu_administration_anonymization_history +msgid "Anonymization History" +msgstr "" + +#. module: anonymization +#: model:ir.model,name:anonymization.model_ir_model_fields_anonymization_history +msgid "ir.model.fields.anonymization.history" +msgstr "" + +#. module: anonymization +#: model:ir.actions.act_window,name:anonymization.action_ir_model_fields_anonymize_wizard +#: view:ir.model.fields.anonymize.wizard:0 +msgid "Anonymize Database" +msgstr "" + +#. module: anonymization +#: field:ir.model.fields.anonymize.wizard,name:0 +msgid "File Name" +msgstr "" + +#. module: anonymization +#: selection:ir.model.fields.anonymization.history,direction:0 +msgid "anonymized -> clear" +msgstr "" + +#. module: anonymization +#: selection:ir.model.fields.anonymization.history,state:0 +msgid "Started" +msgstr "" + +#. module: anonymization +#: selection:ir.model.fields.anonymization.history,state:0 +msgid "Done" +msgstr "" + +#. module: anonymization +#: view:ir.model.fields.anonymization.history:0 +#: field:ir.model.fields.anonymization.history,msg:0 +#: field:ir.model.fields.anonymize.wizard,msg:0 +msgid "Message" +msgstr "" + +#. module: anonymization +#: code:addons/anonymization/anonymization.py:55 +#: sql_constraint:ir.model.fields.anonymization:0 +#, python-format +msgid "You cannot have two fields with the same name on the same object!" +msgstr "" diff --git a/addons/hr/i18n/lt.po b/addons/hr/i18n/lt.po index 84a46bb3a5d..f50c071b739 100644 --- a/addons/hr/i18n/lt.po +++ b/addons/hr/i18n/lt.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-02-08 01:37+0100\n" -"PO-Revision-Date: 2012-05-10 18:17+0000\n" -"Last-Translator: Fabien (Open ERP) \n" +"PO-Revision-Date: 2012-10-21 21:20+0000\n" +"Last-Translator: mchip \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-19 05:20+0000\n" +"X-Launchpad-Export-Date: 2012-10-22 04:40+0000\n" "X-Generator: Launchpad (build 16165)\n" #. module: hr @@ -103,7 +103,7 @@ msgstr "" #. module: hr #: model:ir.actions.todo.category,name:hr.category_hr_management_config msgid "HR Management" -msgstr "" +msgstr "Žmogiškųjų išteklių valdymas" #. module: hr #: help:hr.employee,partner_id:0 @@ -130,7 +130,7 @@ msgstr "Pastabos" #. module: hr #: selection:hr.employee,marital:0 msgid "Married" -msgstr "" +msgstr "Vedęs" #. module: hr #: model:ir.actions.act_window,help:hr.action_create_hr_employee_installer @@ -152,7 +152,7 @@ msgstr "" #. module: hr #: field:hr.employee,color:0 msgid "Color Index" -msgstr "" +msgstr "Spalvos indeksas" #. module: hr #: model:process.transition,note:hr.process_transition_employeeuser0 @@ -169,7 +169,7 @@ msgstr "" #. module: hr #: field:hr.employee,identification_id:0 msgid "Identification No" -msgstr "" +msgstr "Asmens kodas" #. module: hr #: selection:hr.employee,gender:0 @@ -206,7 +206,7 @@ msgstr "Žemesniosios kategorijos" #: view:hr.job:0 field:hr.job,description:0 #: model:ir.model,name:hr.model_hr_job msgid "Job Description" -msgstr "" +msgstr "Darbo aprašymas" #. module: hr #: field:hr.employee,work_location:0 diff --git a/addons/hr_attendance/report/attendance_by_month.py b/addons/hr_attendance/report/attendance_by_month.py index 17cf548345e..fe6d09a6084 100644 --- a/addons/hr_attendance/report/attendance_by_month.py +++ b/addons/hr_attendance/report/attendance_by_month.py @@ -86,6 +86,8 @@ class report_custom(report_rml): for att in attendences: dt = datetime.strptime(att['name'], '%Y-%m-%d %H:%M:%S') if ldt and att['action'] == 'sign_out': + if dt.date() > ldt.date(): + dt = ldt wh += (float((dt - ldt).seconds)/60/60) else: ldt = dt diff --git a/addons/hr_attendance/report/timesheet.py b/addons/hr_attendance/report/timesheet.py index 900f982dc74..326ffce63e0 100644 --- a/addons/hr_attendance/report/timesheet.py +++ b/addons/hr_attendance/report/timesheet.py @@ -38,7 +38,7 @@ class report_custom(report_rml): def create_xml(self, cr, uid, ids, datas, context=None): obj_emp = pooler.get_pool(cr.dbname).get('hr.employee') - + ids = datas['active_ids'] start_date = datetime.strptime(datas['form']['init_date'], '%Y-%m-%d') end_date = datetime.strptime(datas['form']['end_date'], '%Y-%m-%d') first_monday = start_date - relativedelta(days=start_date.date().weekday()) diff --git a/addons/hr_attendance/wizard/hr_attendance_byweek.py b/addons/hr_attendance/wizard/hr_attendance_byweek.py index 9ee69a937d1..454ba7764da 100644 --- a/addons/hr_attendance/wizard/hr_attendance_byweek.py +++ b/addons/hr_attendance/wizard/hr_attendance_byweek.py @@ -37,6 +37,7 @@ class hr_attendance_byweek(osv.osv_memory): def print_report(self, cr, uid, ids, context=None): datas = { 'ids': [], + 'active_ids': context['active_ids'], 'model': 'hr.employee', 'form': self.read(cr, uid, ids)[0] } diff --git a/addons/hr_recruitment/hr_recruitment_data.xml b/addons/hr_recruitment/hr_recruitment_data.xml index 288231a4000..330c86652dd 100644 --- a/addons/hr_recruitment/hr_recruitment_data.xml +++ b/addons/hr_recruitment/hr_recruitment_data.xml @@ -313,7 +313,7 @@ You can automatically receive job application though an email gateway, see the H This question requires an answer. - Exprience + Experience comment The comment you entered is in an invalid format. diff --git a/addons/hr_timesheet/i18n/mk.po b/addons/hr_timesheet/i18n/mk.po new file mode 100644 index 00000000000..21924603c20 --- /dev/null +++ b/addons/hr_timesheet/i18n/mk.po @@ -0,0 +1,642 @@ +# Macedonian 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 , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-10-20 13:07+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Macedonian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-10-21 04:41+0000\n" +"X-Generator: Launchpad (build 16165)\n" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:43 +#: code:addons/hr_timesheet/report/users_timesheet.py:77 +#, python-format +msgid "Wed" +msgstr "Сре" + +#. module: hr_timesheet +#: view:hr.sign.out.project:0 +msgid "(Keep empty for current_time)" +msgstr "" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/wizard/hr_timesheet_sign_in_out.py:132 +#, python-format +msgid "No employee defined for your user !" +msgstr "" + +#. module: hr_timesheet +#: view:hr.analytic.timesheet:0 +msgid "Group By..." +msgstr "Групирај По..." + +#. module: hr_timesheet +#: model:ir.actions.act_window,help:hr_timesheet.action_hr_timesheet_sign_in +msgid "" +"Employees can encode their time spent on the different projects. A project " +"is an analytic account and the time spent on a project generate costs on the " +"analytic account. This feature allows to record at the same time the " +"attendance and the timesheet." +msgstr "" + +#. module: hr_timesheet +#: view:hr.analytic.timesheet:0 +msgid "Today" +msgstr "Денес" + +#. module: hr_timesheet +#: field:hr.employee,journal_id:0 +msgid "Analytic Journal" +msgstr "Аналитичка картица" + +#. module: hr_timesheet +#: view:hr.sign.out.project:0 +msgid "Stop Working" +msgstr "" + +#. module: hr_timesheet +#: model:ir.actions.act_window,name:hr_timesheet.action_hr_timesheet_employee +#: model:ir.ui.menu,name:hr_timesheet.menu_hr_timesheet_employee +msgid "Employee Timesheet" +msgstr "" + +#. module: hr_timesheet +#: view:account.analytic.account:0 +msgid "Work done stats" +msgstr "" + +#. module: hr_timesheet +#: view:hr.analytic.timesheet:0 +#: model:ir.ui.menu,name:hr_timesheet.menu_hr_reporting_timesheet +msgid "Timesheet" +msgstr "" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:43 +#: code:addons/hr_timesheet/report/users_timesheet.py:77 +#, python-format +msgid "Mon" +msgstr "Пон" + +#. module: hr_timesheet +#: view:hr.sign.in.project:0 +msgid "Sign in" +msgstr "Најава" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:43 +#: code:addons/hr_timesheet/report/users_timesheet.py:77 +#, python-format +msgid "Fri" +msgstr "Пет" + +#. module: hr_timesheet +#: field:hr.employee,uom_id:0 +msgid "UoM" +msgstr "" + +#. module: hr_timesheet +#: view:hr.sign.in.project:0 +msgid "" +"Employees can encode their time spent on the different projects they are " +"assigned on. A project is an analytic account and the time spent on a " +"project generates costs on the analytic account. This feature allows to " +"record at the same time the attendance and the timesheet." +msgstr "" + +#. module: hr_timesheet +#: field:hr.sign.out.project,analytic_amount:0 +msgid "Minimum Analytic Amount" +msgstr "" + +#. module: hr_timesheet +#: view:hr.analytical.timesheet.employee:0 +msgid "Monthly Employee Timesheet" +msgstr "" + +#. module: hr_timesheet +#: view:hr.sign.out.project:0 +msgid "Work done in the last period" +msgstr "" + +#. module: hr_timesheet +#: field:hr.sign.in.project,state:0 +#: field:hr.sign.out.project,state:0 +msgid "Current state" +msgstr "Тековен статус" + +#. module: hr_timesheet +#: field:hr.sign.in.project,name:0 +#: field:hr.sign.out.project,name:0 +msgid "Employees name" +msgstr "Име на Вработениот" + +#. module: hr_timesheet +#: field:hr.sign.out.project,account_id:0 +msgid "Project / Analytic Account" +msgstr "" + +#. module: hr_timesheet +#: model:ir.model,name:hr_timesheet.model_hr_analytical_timesheet_users +msgid "Print Employees Timesheet" +msgstr "" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/hr_timesheet.py:175 +#: code:addons/hr_timesheet/hr_timesheet.py:177 +#, python-format +msgid "Warning !" +msgstr "Внимание !" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/wizard/hr_timesheet_sign_in_out.py:77 +#: code:addons/hr_timesheet/wizard/hr_timesheet_sign_in_out.py:132 +#, python-format +msgid "UserError" +msgstr "Корисничка Грешка" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/wizard/hr_timesheet_sign_in_out.py:77 +#, python-format +msgid "No cost unit defined for this employee !" +msgstr "" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:43 +#: code:addons/hr_timesheet/report/users_timesheet.py:77 +#, python-format +msgid "Tue" +msgstr "Вто" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/wizard/hr_timesheet_print_employee.py:42 +#, python-format +msgid "Warning" +msgstr "Предупредување" + +#. module: hr_timesheet +#: field:hr.analytic.timesheet,partner_id:0 +msgid "Partner" +msgstr "Партнер" + +#. module: hr_timesheet +#: view:hr.sign.in.project:0 +#: view:hr.sign.out.project:0 +msgid "Sign In/Out by Project" +msgstr "" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:43 +#: code:addons/hr_timesheet/report/users_timesheet.py:77 +#, python-format +msgid "Sat" +msgstr "Саб" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:43 +#: code:addons/hr_timesheet/report/users_timesheet.py:77 +#, python-format +msgid "Sun" +msgstr "Нед" + +#. module: hr_timesheet +#: view:hr.analytic.timesheet:0 +msgid "Analytic account" +msgstr "Аналитичко конто" + +#. module: hr_timesheet +#: view:hr.analytical.timesheet.employee:0 +#: view:hr.analytical.timesheet.users:0 +msgid "Print" +msgstr "Печати" + +#. module: hr_timesheet +#: view:hr.analytic.timesheet:0 +#: model:ir.actions.act_window,name:hr_timesheet.act_hr_timesheet_line_evry1_all_form +#: model:ir.ui.menu,name:hr_timesheet.menu_hr_working_hours +msgid "Timesheet Lines" +msgstr "" + +#. module: hr_timesheet +#: view:hr.analytical.timesheet.users:0 +msgid "Monthly Employees Timesheet" +msgstr "" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:40 +#: code:addons/hr_timesheet/report/users_timesheet.py:73 +#: selection:hr.analytical.timesheet.employee,month:0 +#: selection:hr.analytical.timesheet.users,month:0 +#, python-format +msgid "July" +msgstr "Јули" + +#. module: hr_timesheet +#: field:hr.sign.in.project,date:0 +#: field:hr.sign.out.project,date_start:0 +msgid "Starting Date" +msgstr "Почетен датум" + +#. module: hr_timesheet +#: view:hr.employee:0 +msgid "Categories" +msgstr "Категории" + +#. module: hr_timesheet +#: constraint:hr.analytic.timesheet:0 +msgid "You cannot modify an entry in a Confirmed/Done timesheet !." +msgstr "" + +#. module: hr_timesheet +#: help:hr.employee,product_id:0 +msgid "Specifies employee's designation as a product with type 'service'." +msgstr "" + +#. module: hr_timesheet +#: view:hr.analytic.timesheet:0 +msgid "Total cost" +msgstr "" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:40 +#: code:addons/hr_timesheet/report/users_timesheet.py:73 +#: selection:hr.analytical.timesheet.employee,month:0 +#: selection:hr.analytical.timesheet.users,month:0 +#, python-format +msgid "September" +msgstr "Септември" + +#. module: hr_timesheet +#: model:ir.model,name:hr_timesheet.model_hr_analytic_timesheet +msgid "Timesheet Line" +msgstr "" + +#. module: hr_timesheet +#: field:hr.analytical.timesheet.users,employee_ids:0 +msgid "employees" +msgstr "" + +#. module: hr_timesheet +#: view:account.analytic.account:0 +msgid "Stats by month" +msgstr "" + +#. module: hr_timesheet +#: view:account.analytic.account:0 +#: field:hr.analytical.timesheet.employee,month:0 +#: field:hr.analytical.timesheet.users,month:0 +msgid "Month" +msgstr "Месец" + +#. module: hr_timesheet +#: field:hr.sign.out.project,info:0 +msgid "Work Description" +msgstr "" + +#. module: hr_timesheet +#: view:account.analytic.account:0 +msgid "Invoice Analysis" +msgstr "" + +#. module: hr_timesheet +#: model:ir.actions.report.xml,name:hr_timesheet.report_user_timesheet +msgid "Employee timesheet" +msgstr "" + +#. module: hr_timesheet +#: model:ir.actions.act_window,name:hr_timesheet.action_hr_timesheet_sign_in +#: model:ir.actions.act_window,name:hr_timesheet.action_hr_timesheet_sign_out +msgid "Sign in / Sign out by project" +msgstr "" + +#. module: hr_timesheet +#: model:ir.actions.act_window,name:hr_timesheet.action_define_analytic_structure +msgid "Define your Analytic Structure" +msgstr "" + +#. module: hr_timesheet +#: view:hr.sign.in.project:0 +msgid "Sign in / Sign out" +msgstr "Најава / Одјава" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/hr_timesheet.py:175 +#, python-format +msgid "" +"Analytic journal is not defined for employee %s \n" +"Define an employee for the selected user and assign an analytic journal!" +msgstr "" + +#. module: hr_timesheet +#: view:hr.sign.in.project:0 +msgid "(Keep empty for current time)" +msgstr "" + +#. module: hr_timesheet +#: view:hr.employee:0 +msgid "Timesheets" +msgstr "" + +#. module: hr_timesheet +#: model:ir.actions.act_window,help:hr_timesheet.action_define_analytic_structure +msgid "" +"You should create an analytic account structure depending on your needs to " +"analyse costs and revenues. In OpenERP, analytic accounts are also used to " +"track customer contracts." +msgstr "" + +#. module: hr_timesheet +#: field:hr.analytic.timesheet,line_id:0 +msgid "Analytic Line" +msgstr "" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:40 +#: code:addons/hr_timesheet/report/users_timesheet.py:73 +#: selection:hr.analytical.timesheet.employee,month:0 +#: selection:hr.analytical.timesheet.users,month:0 +#, python-format +msgid "August" +msgstr "Август" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:40 +#: code:addons/hr_timesheet/report/users_timesheet.py:73 +#: selection:hr.analytical.timesheet.employee,month:0 +#: selection:hr.analytical.timesheet.users,month:0 +#, python-format +msgid "June" +msgstr "Јуни" + +#. module: hr_timesheet +#: view:hr.analytical.timesheet.employee:0 +msgid "Print My Timesheet" +msgstr "" + +#. module: hr_timesheet +#: view:hr.analytic.timesheet:0 +msgid "Date" +msgstr "Датум" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:40 +#: code:addons/hr_timesheet/report/users_timesheet.py:73 +#: selection:hr.analytical.timesheet.employee,month:0 +#: selection:hr.analytical.timesheet.users,month:0 +#, python-format +msgid "November" +msgstr "Ноември" + +#. module: hr_timesheet +#: constraint:hr.employee:0 +msgid "Error ! You cannot create recursive Hierarchy of Employees." +msgstr "Грешка! Не можете да креирате рекурзивна хиерархија на вработените." + +#. module: hr_timesheet +#: field:hr.sign.out.project,date:0 +msgid "Closing Date" +msgstr "" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:40 +#: code:addons/hr_timesheet/report/users_timesheet.py:73 +#: selection:hr.analytical.timesheet.employee,month:0 +#: selection:hr.analytical.timesheet.users,month:0 +#, python-format +msgid "October" +msgstr "Октомври" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:40 +#: code:addons/hr_timesheet/report/users_timesheet.py:73 +#: selection:hr.analytical.timesheet.employee,month:0 +#: selection:hr.analytical.timesheet.users,month:0 +#, python-format +msgid "January" +msgstr "Јануари" + +#. module: hr_timesheet +#: view:account.analytic.account:0 +msgid "Key dates" +msgstr "" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:43 +#: code:addons/hr_timesheet/report/users_timesheet.py:77 +#, python-format +msgid "Thu" +msgstr "Чет" + +#. module: hr_timesheet +#: view:account.analytic.account:0 +msgid "Analysis stats" +msgstr "" + +#. module: hr_timesheet +#: model:ir.model,name:hr_timesheet.model_hr_analytical_timesheet_employee +msgid "Print Employee Timesheet & Print My Timesheet" +msgstr "" + +#. module: hr_timesheet +#: field:hr.sign.in.project,emp_id:0 +#: field:hr.sign.out.project,emp_id:0 +msgid "Employee ID" +msgstr "Идентификационен Број на Вработениот" + +#. module: hr_timesheet +#: view:hr.sign.out.project:0 +msgid "General Information" +msgstr "Општи информации" + +#. module: hr_timesheet +#: model:ir.actions.act_window,name:hr_timesheet.action_hr_timesheet_my +msgid "My Current Timesheet" +msgstr "" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:40 +#: code:addons/hr_timesheet/report/users_timesheet.py:73 +#: selection:hr.analytical.timesheet.employee,month:0 +#: selection:hr.analytical.timesheet.users,month:0 +#, python-format +msgid "December" +msgstr "Декември" + +#. module: hr_timesheet +#: view:hr.analytical.timesheet.employee:0 +#: view:hr.analytical.timesheet.users:0 +#: view:hr.sign.in.project:0 +#: view:hr.sign.out.project:0 +msgid "Cancel" +msgstr "Откажи" + +#. module: hr_timesheet +#: model:ir.actions.act_window,name:hr_timesheet.action_hr_timesheet_users +#: model:ir.actions.report.xml,name:hr_timesheet.report_users_timesheet +#: model:ir.actions.wizard,name:hr_timesheet.wizard_hr_timesheet_users +#: model:ir.ui.menu,name:hr_timesheet.menu_hr_timesheet_users +msgid "Employees Timesheet" +msgstr "" + +#. module: hr_timesheet +#: view:hr.analytic.timesheet:0 +msgid "Information" +msgstr "Информации" + +#. module: hr_timesheet +#: field:hr.analytical.timesheet.employee,employee_id:0 +#: model:ir.model,name:hr_timesheet.model_hr_employee +msgid "Employee" +msgstr "Вработен" + +#. module: hr_timesheet +#: model:ir.actions.act_window,help:hr_timesheet.act_hr_timesheet_line_evry1_all_form +msgid "" +"Through this menu you can register and follow your workings hours by project " +"every day." +msgstr "" + +#. module: hr_timesheet +#: field:hr.sign.in.project,server_date:0 +#: field:hr.sign.out.project,server_date:0 +msgid "Current Date" +msgstr "Тековен датум" + +#. module: hr_timesheet +#: view:hr.analytical.timesheet.employee:0 +msgid "This wizard will print monthly timesheet" +msgstr "" + +#. module: hr_timesheet +#: view:hr.analytic.timesheet:0 +#: field:hr.employee,product_id:0 +msgid "Product" +msgstr "Продукт" + +#. module: hr_timesheet +#: view:hr.analytic.timesheet:0 +msgid "Invoicing" +msgstr "Фактурирање" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:40 +#: code:addons/hr_timesheet/report/users_timesheet.py:73 +#: selection:hr.analytical.timesheet.employee,month:0 +#: selection:hr.analytical.timesheet.users,month:0 +#, python-format +msgid "May" +msgstr "Мај" + +#. module: hr_timesheet +#: view:hr.analytic.timesheet:0 +msgid "Total time" +msgstr "Вкупно време" + +#. module: hr_timesheet +#: view:hr.sign.in.project:0 +msgid "(local time on the server side)" +msgstr "" + +#. module: hr_timesheet +#: model:ir.model,name:hr_timesheet.model_hr_sign_in_project +msgid "Sign In By Project" +msgstr "" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:40 +#: code:addons/hr_timesheet/report/users_timesheet.py:73 +#: selection:hr.analytical.timesheet.employee,month:0 +#: selection:hr.analytical.timesheet.users,month:0 +#, python-format +msgid "February" +msgstr "Февруари" + +#. module: hr_timesheet +#: model:ir.model,name:hr_timesheet.model_hr_sign_out_project +msgid "Sign Out By Project" +msgstr "" + +#. module: hr_timesheet +#: view:hr.analytical.timesheet.users:0 +msgid "Employees" +msgstr "Вработени" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:40 +#: code:addons/hr_timesheet/report/users_timesheet.py:73 +#: selection:hr.analytical.timesheet.employee,month:0 +#: selection:hr.analytical.timesheet.users,month:0 +#, python-format +msgid "March" +msgstr "Март" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/report/user_timesheet.py:40 +#: code:addons/hr_timesheet/report/users_timesheet.py:73 +#: selection:hr.analytical.timesheet.employee,month:0 +#: selection:hr.analytical.timesheet.users,month:0 +#, python-format +msgid "April" +msgstr "Април" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/hr_timesheet.py:177 +#, python-format +msgid "" +"No analytic account defined on the project.\n" +"Please set one or we can not automatically fill the timesheet." +msgstr "" + +#. module: hr_timesheet +#: view:account.analytic.account:0 +#: view:hr.analytic.timesheet:0 +msgid "Users" +msgstr "Корисници" + +#. module: hr_timesheet +#: view:hr.sign.in.project:0 +msgid "Start Working" +msgstr "" + +#. module: hr_timesheet +#: view:account.analytic.account:0 +msgid "Stats by user" +msgstr "" + +#. module: hr_timesheet +#: code:addons/hr_timesheet/wizard/hr_timesheet_print_employee.py:42 +#, python-format +msgid "No employee defined for this user" +msgstr "" + +#. module: hr_timesheet +#: field:hr.analytical.timesheet.employee,year:0 +#: field:hr.analytical.timesheet.users,year:0 +msgid "Year" +msgstr "Година" + +#. module: hr_timesheet +#: view:hr.analytic.timesheet:0 +msgid "Accounting" +msgstr "Сметководство" + +#. module: hr_timesheet +#: view:hr.sign.out.project:0 +msgid "Change Work" +msgstr "" + +#~ msgid "Sum" +#~ msgstr "Вкупно" + +#~ msgid "Total" +#~ msgstr "Вкупно" diff --git a/addons/hr_timesheet/wizard/hr_timesheet_sign_in_out.py b/addons/hr_timesheet/wizard/hr_timesheet_sign_in_out.py index e92e88bdcbd..031a17baee0 100644 --- a/addons/hr_timesheet/wizard/hr_timesheet_sign_in_out.py +++ b/addons/hr_timesheet/wizard/hr_timesheet_sign_in_out.py @@ -147,7 +147,7 @@ class hr_si_project(osv.osv_memory): 'view_type': 'form', 'view_mode': 'tree,form', 'res_model': 'hr.sign.%s.project' % in_out, - 'views': [(False,'tree'), (resource_id,'form')], + 'views': [(resource_id,'form')], 'type': 'ir.actions.act_window', 'target': 'new' } diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index ab76f2b0f64..30e4ff40885 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -176,7 +176,7 @@ account_analytic_line() class hr_analytic_timesheet(osv.osv): _inherit = "hr.analytic.timesheet" - def on_change_account_id(self, cr, uid, ids, account_id): + def on_change_account_id(self, cr, uid, ids, account_id, user_id=False): res = {} if not account_id: return res diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml b/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml index b752ade8da0..95fa39a6eda 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml @@ -50,7 +50,7 @@ - + @@ -72,7 +72,7 @@ - + diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py index 96cb15ddce1..e62134ee38a 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py @@ -388,7 +388,7 @@ class hr_timesheet_line(osv.osv): return True def multi_on_change_account_id(self, cr, uid, ids, account_ids, context=None): - return dict([(el, self.on_change_account_id(cr, uid, ids, el)) for el in account_ids]) + return dict([(el, self.on_change_account_id(cr, uid, ids, el, context.get('user_id', uid))) for el in account_ids]) hr_timesheet_line() diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml index b247805d50d..fc35a17377f 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml @@ -97,7 +97,7 @@ - + @@ -110,7 +110,7 @@
- + diff --git a/addons/hr_timesheet_sheet/report/timesheet_report.py b/addons/hr_timesheet_sheet/report/timesheet_report.py index 497fe8da117..cfd87d784ad 100644 --- a/addons/hr_timesheet_sheet/report/timesheet_report.py +++ b/addons/hr_timesheet_sheet/report/timesheet_report.py @@ -46,6 +46,7 @@ class timesheet_report(osv.osv): 'department_id':fields.many2one('hr.department','Department',readonly=True), 'date_from': fields.date('Date from',readonly=True,), 'date_to': fields.date('Date to',readonly=True), + 'date_current': fields.date('Current date', required=True), 'state' : fields.selection([ ('new', 'New'), ('draft','Draft'), @@ -64,6 +65,9 @@ class timesheet_report(osv.osv): htss.name, htss.date_from, htss.date_to, + to_char(htss.date_from, 'YYYY-MM-DD') as day, + to_char(htss.date_from, 'YYYY') as year, + to_char(htss.date_from, 'MM') as month, count(*) as nbr, aal.unit_amount as quantity, aal.amount as cost, diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 989b63afee5..f561919939d 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -45,7 +45,7 @@ class mail_message(osv.Model): _inherit = ['ir.needaction_mixin'] _order = 'id desc' - _message_read_limit = 10 + _message_read_limit = 15 _message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read', 'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name', 'favorite_user_ids'] _message_record_name_length = 18 @@ -77,8 +77,7 @@ class mail_message(osv.Model): notif_obj = self.pool.get('mail.notification') notif_ids = notif_obj.search(cr, uid, [ ('partner_id', 'in', [partner_id]), - ('message_id', 'in', ids), - ('read', '=', True) + ('message_id', 'in', ids) ], context=context) for notif in notif_obj.browse(cr, uid, notif_ids, context=context): res[notif.message_id.id] = not notif.read @@ -163,13 +162,14 @@ class mail_message(osv.Model): #------------------------------------------------------ def vote_toggle(self, cr, uid, ids, context=None): - ''' Toggles vote. Performed using read to avoid access rights issues. ''' + ''' Toggles vote. Performed using read to avoid access rights issues. + Done as SUPERUSER_ID because uid may vote for a message he cannot modify. ''' for message in self.read(cr, uid, ids, ['vote_user_ids'], context=context): new_has_voted = not (uid in message.get('vote_user_ids')) if new_has_voted: - self.write(cr, uid, message.get('id'), {'vote_user_ids': [(4, uid)]}, context=context) + self.write(cr, SUPERUSER_ID, message.get('id'), {'vote_user_ids': [(4, uid)]}, context=context) else: - self.write(cr, uid, message.get('id'), {'vote_user_ids': [(3, uid)]}, context=context) + self.write(cr, SUPERUSER_ID, message.get('id'), {'vote_user_ids': [(3, uid)]}, context=context) return new_has_voted or False #------------------------------------------------------ @@ -177,13 +177,14 @@ class mail_message(osv.Model): #------------------------------------------------------ def favorite_toggle(self, cr, uid, ids, context=None): - ''' Toggles favorite. Performed using read to avoid access rights issues. ''' + ''' Toggles favorite. Performed using read to avoid access rights issues. + Done as SUPERUSER_ID because uid may star a message he cannot modify. ''' for message in self.read(cr, uid, ids, ['favorite_user_ids'], context=context): new_is_favorite = not (uid in message.get('favorite_user_ids')) if new_is_favorite: - self.write(cr, uid, message.get('id'), {'favorite_user_ids': [(4, uid)]}, context=context) + self.write(cr, SUPERUSER_ID, message.get('id'), {'favorite_user_ids': [(4, uid)]}, context=context) else: - self.write(cr, uid, message.get('id'), {'favorite_user_ids': [(3, uid)]}, context=context) + self.write(cr, SUPERUSER_ID, message.get('id'), {'favorite_user_ids': [(3, uid)]}, context=context) return new_is_favorite or False #------------------------------------------------------ @@ -252,9 +253,11 @@ class mail_message(osv.Model): # result = sorted(result, key=lambda k: k['id']) tree_not = [] # expandable for not show message - for message_id, message in read_messages.iteritems(): - # get all childs - # SHOULD NOT BE SUPERUSED_ID -> check search is correctly implemented in mail.message + id_list = sorted(read_messages.keys()) + for message_id in id_list: + message = read_messages[message_id] + + # TDE note: check search is correctly implemented in mail.message not_loaded_ids = self.search(cr, uid, [ ('parent_id', '=', message['id']), ('id', 'not in', message_loaded_ids), @@ -307,7 +310,7 @@ class mail_message(osv.Model): 'type': 'expandable', 'parent_id': parent_id, 'id': -1, - 'max_limit': True + 'max_limit': True, }) return message_list @@ -342,11 +345,9 @@ class mail_message(osv.Model): further parents :return list: list of trees of messages """ - print '>>> executing message_read' if message_loaded_ids: domain += [('id', 'not in', message_loaded_ids)] limit = limit or self._message_read_limit - limit = 200 read_messages = {} message_list = [] @@ -359,37 +360,36 @@ class mail_message(osv.Model): # TDE FIXME: check access rights on search are implemented for mail.message # fetch messages according to the domain, add their parents if uid has access to - if not ids: - ids = self.search(cr, uid, domain, context=context, limit=limit) - for message in self.read(cr, uid, ids, self._message_read_fields, context=context): - # if not in tree and not in message_loded list - if not read_messages.get(message.get('id')) and message.get('id') not in message_loaded_ids: - read_messages[message.get('id')] = message - message_list.append(self._message_get_dict(cr, uid, message, context=context)) + ids = self.search(cr, uid, domain, context=context, limit=limit) + for message in self.read(cr, uid, ids, self._message_read_fields, context=context): + # if not in tree and not in message_loded list + if not read_messages.get(message.get('id')) and message.get('id') not in message_loaded_ids: + read_messages[message.get('id')] = message + message_list.append(self._message_get_dict(cr, uid, message, context=context)) - # get all parented message if the user have the access - parent = self._get_parent(cr, uid, message, context=context) - while parent and parent.get('id') != parent_id: - if not read_messages.get(parent.get('id')) and parent.get('id') not in message_loaded_ids: - read_messages[parent.get('id')] = parent - message_list.append(self._message_get_dict(cr, uid, parent, context=context)) - parent = self._get_parent(cr, uid, parent, context=context) + # get all parented message if the user have the access + parent = self._get_parent(cr, uid, message, context=context) + while parent and parent.get('id') != parent_id: + if not read_messages.get(parent.get('id')) and parent.get('id') not in message_loaded_ids: + read_messages[parent.get('id')] = parent + message_list.append(self._message_get_dict(cr, uid, parent, context=context)) + parent = self._get_parent(cr, uid, parent, context=context) - # get the child expandable messages for the tree - message_list = sorted(message_list, key=lambda k: k['id']) - message_list = self._message_read_expandable(cr, uid, message_list, read_messages, - message_loaded_ids=message_loaded_ids, domain=domain, context=context, parent_id=parent_id, limit=limit) + # get the child expandable messages for the tree + message_list = sorted(message_list, key=lambda k: k['id']) + message_list = self._message_read_expandable(cr, uid, message_list, read_messages, + message_loaded_ids=message_loaded_ids, domain=domain, context=context, parent_id=parent_id, limit=limit) return message_list # TDE Note: do we need this ? - def user_free_attachment(self, cr, uid, context=None): - attachment = self.pool.get('ir.attachment') - attachment_list = [] - attachment_ids = attachment.search(cr, uid, [('res_model', '=', 'mail.message'), ('create_uid', '=', uid)]) - if len(attachment_ids): - attachment_list = [{'id': attach.id, 'name': attach.name, 'date': attach.create_date} for attach in attachment.browse(cr, uid, attachment_ids, context=context)] - return attachment_list + # def user_free_attachment(self, cr, uid, context=None): + # attachment = self.pool.get('ir.attachment') + # attachment_list = [] + # attachment_ids = attachment.search(cr, uid, [('res_model', '=', 'mail.message'), ('create_uid', '=', uid)]) + # if len(attachment_ids): + # attachment_list = [{'id': attach.id, 'name': attach.name, 'date': attach.create_date} for attach in attachment.browse(cr, uid, attachment_ids, context=context)] + # return attachment_list #------------------------------------------------------ # Email api @@ -410,7 +410,7 @@ class mail_message(osv.Model): - create: if - I am in the document message_follower_ids OR - I can write on the related document if res_model, res_id OR - - I write a mail to another user (no res_model) + - I create a private message (no model, no res_id) - Otherwise: raise - write: if - I can write on the related document if res_model, res_id @@ -448,10 +448,14 @@ class mail_message(osv.Model): if operation == 'read': author_ids = [mid for mid, message in message_values.iteritems() if message.get('author_id') and message.get('author_id') == partner_id] + # Create: Check messages you create that are private messages -> ir.rule ? + elif operation == 'create': + author_ids = [mid for mid, message in message_values.iteritems() + if not message.get('model') and not message.get('res_id')] else: author_ids = [] - # Create: Check message_follower_ids and author_ids + # Create: Check message_follower_ids if operation == 'create': doc_follower_ids = [] for model, mids in model_record_ids.items(): @@ -467,12 +471,6 @@ class mail_message(osv.Model): else: doc_follower_ids = [] - # Create/write: Check author_ids - if operation == 'create' or operation == 'write': - author_ids = author_ids+[mid for mid, message in message_values.iteritems() - if message.get('author_id') and message.get('author_id') == partner_id and not message.get('res_model')] - - # Calculate remaining ids, and related model/res_ids model_record_ids = {} other_ids = set(ids).difference(set(notified_ids), set(author_ids), set(doc_follower_ids)) diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml index fbdfbc27125..4677beee511 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -58,7 +58,7 @@ - mail.followers: read its own entries + mail.followers: read and write its own entries [('partner_id', '=', user.partner_id.id)] diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 22054686145..9dd18b0fec5 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -63,6 +63,9 @@ opacity: 0.8; -webkit-transition: opacity 0.2s linear; } +.openerp .oe_mail .oe_msg.oe_msg_composer .oe_msg_footer{ + height: auto; +} .openerp .oe_mail .oe_msg .oe_msg_content{ display: block; overflow: hidden; diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index e49811ab38c..58f1f82325e 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -7,7 +7,7 @@ openerp.mail = function(session) { openerp_mail_followers(session, mail); // import mail_followers.js /** - * ------------------------------------------------------------ + * ------------------------------------------------------------ * FormView * ------------------------------------------------------------ * @@ -23,7 +23,7 @@ openerp.mail = function(session) { */ var context_keys = ['default_template_id', 'default_composition_mode', 'default_use_template', 'default_partner_ids', 'default_model', - 'default_res_id', 'default_subtype', 'active_id', 'lang', + 'default_res_id', 'default_content_subtype', 'active_id', 'lang', 'bin_raw', 'tz', 'active_model', 'edi_web_url_view', 'active_ids'] for (var key in action.context) { if (_.indexOf(context_keys, key) == -1) { @@ -38,7 +38,7 @@ openerp.mail = function(session) { /** - * ------------------------------------------------------------ + * ------------------------------------------------------------ * ChatterUtils * ------------------------------------------------------------ * @@ -51,17 +51,20 @@ openerp.mail = function(session) { mail.ChatterUtils = { - /** Get an image in /web/binary/image?... */ + /** + *Get an image in /web/binary/image?... */ get_image: function(session, model, field, id) { return session.prefix + '/web/binary/image?session_id=' + session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || ''); }, - /** Get the url of an attachment {'id': id} */ + /** + *Get the url of an attachment {'id': id} */ get_attachment_url: function (session, attachment) { return session.origin + '/web/binary/saveas?session_id=' + session.session_id + '&model=ir.attachment&field=datas&filename_field=datas_fname&id=' + attachment['id']; }, - /** Replaces some expressions + /** + *Replaces some expressions * - :name - shortcut to an image */ do_replace_expressions: function (string) { @@ -91,7 +94,7 @@ openerp.mail = function(session) { /** - * ------------------------------------------------------------ + * ------------------------------------------------------------ * ComposeMessage widget * ------------------------------------------------------------ * @@ -118,21 +121,16 @@ openerp.mail = function(session) { var self = this; this._super(parent); this.context = options.context || {}; + this.options = options.options; - this.datasets = { - 'attachment_ids' : [], - 'id': datasets.id, - 'model': datasets.model, - 'show_compact': true, - 'res_model': datasets.res_model, - 'is_private': datasets.is_private || false, - 'partner_ids': datasets.partner_ids || [], - 'avatar': mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid), - }; - this.options={}; - this.options.display_indented_thread = options.options.display_indented_thread || -1; - this.options.show_attachment_delete = true; - + // data of this compose message + this.attachment_ids = []; + this.id = datasets.id; + this.model = datasets.model; + this.res_model = datasets.res_model; + this.is_private = datasets.is_private || false; + this.partner_ids = datasets.partner_ids || []; + this.avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid); this.parent_thread= parent.messages!= undefined ? parent : false; this.ds_attachment = new session.web.DataSetSearch(this, 'ir.attachment'); @@ -147,12 +145,6 @@ openerp.mail = function(session) { start: function(){ this.$render_compact = this.$el; - if(this.datasets.show_compact) { - this.$render_compact.show(); - } else { - this.$render_compact.hide(); - } - this.bind_events(); }, @@ -162,14 +154,14 @@ openerp.mail = function(session) { var self = this; var render = $(session.web.qweb.render('mail.thread.message.attachments', {'widget': self})); if(!this.list_attachment){ - this.$('.oe_msg_attachment_list').replaceWith( render ); + this.$render_expandable.find('.oe_msg_attachment_list').replaceWith( render ); } else { this.list_attachment.replaceWith( render ); } - this.list_attachment = this.$(".oe_msg_attachments"); + this.list_attachment = this.$render_expandable.find(".oe_msg_attachments"); // event: delete an attachment - this.$el.on('click', '.oe_mail_attachment_delete', self.on_attachment_delete); + this.$render_expandable.on('click', '.oe_mail_attachment_delete', self.on_attachment_delete); }, /* when a user click on the upload button, send file read on_attachment_loaded @@ -184,25 +176,24 @@ openerp.mail = function(session) { // if the files exits for this answer, delete the file before upload var attachments=[]; - for(var i in this.datasets.attachment_ids){ - if((this.datasets.attachment_ids[i].filename || this.datasets.attachment_ids[i].name) == filename){ - if(this.datasets.attachment_ids[i].upload){ + for(var i in this.attachment_ids){ + if((this.attachment_ids[i].filename || this.attachment_ids[i].name) == filename){ + if(this.attachment_ids[i].upload){ return false; } - this.ds_attachment.unlink([this.datasets.attachment_ids[i].id]); + this.ds_attachment.unlink([this.attachment_ids[i].id]); } else { - attachments.push(this.datasets.attachment_ids[i]); + attachments.push(this.attachment_ids[i]); } } - this.datasets.attachment_ids = attachments; + this.attachment_ids = attachments; // submit file - //session.web.blockUI(); - self.$('form.oe_form_binary_form').submit(); + this.$render_expandable.find('form.oe_form_binary_form').submit(); - this.$(".oe_attachment_file").hide(); + this.$render_expandable.find(".oe_attachment_file").hide(); - this.datasets.attachment_ids.push({ + this.attachment_ids.push({ 'id': 0, 'name': filename, 'filename': filename, @@ -216,10 +207,9 @@ openerp.mail = function(session) { /* when the file is uploaded */ on_attachment_loaded: function (event, result) { - //session.web.unblockUI(); - for(var i in this.datasets.attachment_ids){ - if(this.datasets.attachment_ids[i].filename == result.filename && this.datasets.attachment_ids[i].upload) { - this.datasets.attachment_ids[i]={ + for(var i in this.attachment_ids){ + if(this.attachment_ids[i].filename == result.filename && this.attachment_ids[i].upload) { + this.attachment_ids[i]={ 'id': result.id, 'name': result.name, 'filename': result.filename, @@ -229,9 +219,9 @@ openerp.mail = function(session) { } this.display_attachments(); - var $input = this.$('input.oe_form_binary_file'); + var $input = this.$render_expandable.find('input.oe_form_binary_file'); $input.after($input.clone(true)).remove(); - this.$(".oe_attachment_file").show(); + this.$render_expandable.find(".oe_attachment_file").show(); }, /* unlink the file on the server and reload display @@ -241,15 +231,15 @@ openerp.mail = function(session) { var attachment_id=$(event.target).data("id"); if (attachment_id) { var attachments=[]; - for(var i in this.datasets.attachment_ids){ - if(attachment_id!=this.datasets.attachment_ids[i].id){ - attachments.push(this.datasets.attachment_ids[i]); + for(var i in this.attachment_ids){ + if(attachment_id!=this.attachment_ids[i].id){ + attachments.push(this.attachment_ids[i]); } else { this.ds_attachment.unlink([attachment_id]); } } - this.datasets.attachment_ids = attachments; + this.attachment_ids = attachments; this.display_attachments(); } }, @@ -277,25 +267,38 @@ openerp.mail = function(session) { bind_events: function() { var self = this; - // set the function called when attachments are added - this.$el.on('change', 'input.oe_form_binary_file', self.on_attachment_change ); - this.$el.on('click', 'a.oe_cancel', self.on_cancel ); - this.$el.on('click', 'button.oe_post', function(){self.on_message_post()} ); - this.$el.on('click', 'button.oe_full', function(){self.on_compose_fullmail()} ); + this.$render_compact.find('textarea').unbind().on('focus', self.on_compose_expandable); - this.$('textarea.oe_compact').on('focus', self.on_compose_expandable); + if(this.$render_expandable){ + // set the function called when attachments are added + this.$render_expandable.on('change', 'input.oe_form_binary_file', self.on_attachment_change ); + + this.$render_expandable.on('click', 'a.oe_cancel', self.on_cancel ); + this.$render_expandable.on('click', 'button.oe_post', function(){self.on_message_post()} ); + this.$render_expandable.on('click', 'button.oe_full', function(){self.on_compose_fullmail()} ); + + // auto close + this.$render_expandable.on('blur', 'textarea', this.on_compose_expandable); + + /* stack for don't close the compose form if the user click on a button */ + this.$render_expandable.on('focus', 'textarea', function () { self.stay_open = false; }); + this.$render_expandable.on('mousedown', function () { self.stay_open = true; }); + } }, on_compose_fullmail: function(){ var attachments=[]; - for(var i in this.datasets.attachment_ids){ - attachments.push(this.datasets.attachment_ids[i].id); + for(var i in this.attachment_ids){ + attachments.push(this.attachment_ids[i].id); } + /* TDE note: I think this is not necessary, because + * 1/ post on a document: followers added server-side in _notify + * 2/ reply to a message: mail.compose.message should add the previous partners + */ var partner_ids=[]; - for(var i in this.datasets.partner_ids){ - partner_ids.push(this.datasets.partner_ids[i][0]); + for(var i in this.partner_ids){ + partner_ids.push(this.partner_ids[i][0]); } - var action = { type: 'ir.actions.act_window', res_model: 'mail.compose.message', @@ -305,11 +308,11 @@ openerp.mail = function(session) { views: [[false, 'form']], target: 'new', context: { - 'default_model': false, - 'default_res_id': 0, + 'default_model': this.context.default_model, + 'default_res_id': this.context.default_res_id, 'default_content_subtype': 'html', - 'default_parent_id': this.datasets.id, - 'default_body': mail.ChatterUtils.get_text2html(this.$('textarea').val() || ''), + 'default_parent_id': this.id, + 'default_body': mail.ChatterUtils.get_text2html(this.$render_expandable ? (this.$render_expandable.find('textarea').val() || '') : ''), 'default_attachment_ids': attachments, 'default_partner_ids': partner_ids }, @@ -319,11 +322,14 @@ openerp.mail = function(session) { on_cancel: function(event){ if(event) event.stopPropagation(); - this.$('textarea').val(""); - this.$('input[data-id]').remove(); - //this.attachment_ids=[]; + this.$render_expandable.find('textarea').val(""); + this.$render_expandable.find('input[data-id]').remove(); + + this.attachment_ids=[]; this.display_attachments(); - this.$el.hide(); + + this.stay_open = false; + this.on_compose_expandable(); }, /*post a message and fetch the message*/ @@ -331,21 +337,22 @@ openerp.mail = function(session) { var self = this; if (! body) { - var comment_node = this.$('textarea'); + var comment_node = this.$render_expandable.find('textarea'); var body = comment_node.val(); comment_node.val(''); } var attachments=[]; - for(var i in this.datasets.attachment_ids){ - if(this.datasets.attachment_ids[i].upload){ + for(var i in this.attachment_ids){ + if(this.attachment_ids[i].upload){ session.web.dialog($('
' + session.web.qweb.render('CrashManager.warning', {message: 'Please, wait while the file is uploading.'}) + '
')); return false; } - attachments.push(this.datasets.attachment_ids[i].id); + attachments.push(this.attachment_ids[i].id); } if(body.match(/\S+/)) { + //session.web.blockUI(); this.parent_thread.ds_thread.call('message_post_api', [ this.context.default_res_id, mail.ChatterUtils.get_text2html(body), @@ -357,8 +364,8 @@ openerp.mail = function(session) { this.parent_thread.context ]).then(function(records){ self.parent_thread.switch_new_message(records); - self.datasets.attachment_ids=[]; self.on_cancel(); + //session.web.unblockUI(); }); return true; } @@ -377,13 +384,10 @@ openerp.mail = function(session) { this.$render_expandable.insertAfter( this.$render_compact ); this.display_attachments(); - - this.$render_expandable.on('blur', 'textarea', this.on_compose_expandable); - /* stack for don't close the compose form if the user click on a button */ - this.$render_expandable.on('focus', 'textarea', function () { self.stay_open = false; }); - this.$render_expandable.on('mousedown', function () { self.stay_open = true; }); + this.bind_events(); } + if(this.$render_expandable.is(':hidden')){ this.$render_expandable.show(); @@ -392,11 +396,14 @@ openerp.mail = function(session) { } else if(!this.stay_open){ - this.$render_expandable.hide(); - if(this.datasets.show_compact) { - this.$render_compact.show(); - } else { - this.$render_compact.hide(); + // do not close the box if there are some text + if(!this.$render_expandable.find('textarea').val().match(/\S+/)){ + this.$render_expandable.hide(); + if(this.options.show_compact_message) { + this.$render_compact.show(); + } else { + this.$render_compact.hide(); + } } } @@ -405,17 +412,19 @@ openerp.mail = function(session) { do_hide_compact: function() { this.$render_compact.hide(); - this.datasets.show_compact = false; + this.options.show_compact_message = false; }, do_show_compact: function() { - this.$render_compact.show(); - this.datasets.show_compact = true; + if(!this.$render_expandable || this.$render_expandable.is(':hidden')){ + this.$render_compact.show(); + } + this.options.show_compact_message = true; } }); - /** - * ------------------------------------------------------------ + /** + * ------------------------------------------------------------ * Thread Message Expandable Widget * ------------------------------------------------------------ * @@ -438,17 +447,15 @@ openerp.mail = function(session) { default_res_id: 0, default_parent_id: false }, options.context || {}); - this.datasets = { - 'id' : datasets.id || -1, - 'model' : datasets.model || false, - 'parent_id' : datasets.parent_id || false, - 'nb_messages' : datasets.nb_messages || 0, - 'type' : 'expandable', - 'max_limit' : datasets.max_limit || false, - 'flag_used' : false, - }; - - // record options and data + // data of this expandable message + this.id = datasets.id || -1, + this.model = datasets.model || false, + this.parent_id = datasets.parent_id || false, + this.nb_messages = datasets.nb_messages || 0, + this.thread_level = datasets.thread_level || 0, + this.type = 'expandable', + this.max_limit = datasets.max_limit || false, + this.flag_used = false, this.parent_thread= parent.messages!= undefined ? parent : options.options._parents[0] ; }, @@ -483,19 +490,19 @@ openerp.mail = function(session) { */ on_expandable: function (event) { if(event)event.stopPropagation(); - if(this.datasets.flag_used) { + if(this.flag_used) { return false } - this.datasets.flag_used = true; + this.flag_used = true; this.animated_destroy({'fadeTime':300}); - this.parent_thread.message_fetch(false, this.domain, this.context); + this.parent_thread.message_fetch(this.domain, this.context); return false; }, }); - /** - * ------------------------------------------------------------ + /** + * ------------------------------------------------------------ * Thread Message Widget * ------------------------------------------------------------ * This widget handles the display of a messages in a thread. @@ -524,9 +531,6 @@ openerp.mail = function(session) { * @param {Object} [options] * @param {Object} [thread] read obout mail.Thread object * @param {Object} [message] - * @param {Number} [message_ids=null] ids for message_fetch - * @param {Number} [message_data=null] already formatted message data, - * for subthreads getting data from their parent * @param {Number} [truncate_limit=250] number of character to * display before having a "show more" link; note that the text * will not be truncated if it does not have 110% of the parameter @@ -537,28 +541,26 @@ openerp.mail = function(session) { init: function(parent, datasets, options) { this._super(parent); - // record datasets - this.datasets = _.extend({ - 'id' : -1, - 'model' : false, - 'parent_id': false, - 'res_id' : false, - 'type' : false, - 'is_author' : false, - 'is_private' : false, - 'subject' : false, - 'name' : false, - 'record_name' : false, - 'body' : false, - 'vote_nb' :0, - 'has_voted' : false, - 'is_favorite' : false, - 'thread_level' : 0, - 'to_read' : true, - 'author_id' : [], - 'attachment_ids' : [], - }, datasets || {}); - this.datasets._date = datasets.date; + // data of this message + this.id = datasets.id || -1, + this.model = datasets.model || false, + this.parent_id = datasets.parent_id || false, + this.res_id = datasets.res_id || false, + this.type = datasets.type || false, + this.is_author = datasets.is_author || false, + this.is_private = datasets.is_private || false, + this.subject = datasets.subject || false, + this.name = datasets.name || false, + this.record_name = datasets.record_name || false, + this.body = datasets.body || false, + this.vote_nb = datasets.vote_nb || 0, + this.has_voted = datasets.has_voted || false, + this.is_favorite = datasets.is_favorite || false, + this.thread_level = datasets.thread_level || 0, + this.to_read = datasets.to_read || false, + this.author_id = datasets.author_id || [], + this.attachment_ids = datasets.attachment_ids || [], + this._date = datasets.date; // record domain and context this.domain = options.domain || []; @@ -568,23 +570,16 @@ openerp.mail = function(session) { default_parent_id: false }, options.context || {}); // record options - this.options = _.extend(options.options, { - 'message_ids': options.options.message_ids || null, - 'message_data': options.options.message_data || null, - 'show_record_name': options.options.show_record_name != undefined ? options.options.show_record_name: true, - 'truncate_limit': options.options.truncate_limit || 250, - 'show_reply_button': options.options.show_reply_button || -1, - 'show_read_unread_button': options.options.show_read_unread_button || -1 - }); + this.options = options.options; - this.datasets.show_reply_button = this.options.show_reply_button>this.datasets.thread_level; - this.datasets.show_read_unread_button = this.options.show_read_unread_button>this.datasets.thread_level; + this.show_reply_button = this.options.show_compose_message && this.options.show_reply_button > this.thread_level; + this.show_read_unread_button = this.options.show_read_unread_button > this.thread_level; // record options and data this.parent_thread= parent.messages!= undefined ? parent : options.options._parents[0]; this.thread = false; - if( this.datasets.id > 0 ) { + if( this.id > 0 ) { this.formating_data(); } @@ -593,18 +588,21 @@ openerp.mail = function(session) { this.ds_follow = new session.web.DataSetSearch(this, 'mail.followers'); }, + /** + *Convert date, timerelative and avatar in displayable data. + */ formating_data: function(){ //formating and add some fields for render - this.datasets.date = session.web.format_value(this.datasets._date, {type:"datetime"}); - this.datasets.timerelative = $.timeago(this.datasets.date); - if (this.datasets.type == 'email') { - this.datasets.avatar = ('/mail/static/src/img/email_icon.png'); + this.date = session.web.format_value(this._date, {type:"datetime"}); + this.timerelative = $.timeago(this.date); + if (this.type == 'email') { + this.avatar = ('/mail/static/src/img/email_icon.png'); } else { - this.datasets.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.datasets.author_id[0]); + this.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.author_id[0]); } - for (var l in this.datasets.attachment_ids) { - var attach = this.datasets.attachment_ids[l]; + for (var l in this.attachment_ids) { + var attach = this.attachment_ids[l]; attach['url'] = mail.ChatterUtils.get_attachment_url(this.session, attach); } }, @@ -628,20 +626,23 @@ openerp.mail = function(session) { self.$('.oe_msg_attachments:first').toggle(); }); // event: click on icone 'Read' in header - this.$el.on('click', 'a.oe_read', this.on_message_read_unread); + this.$el.on('click', '.oe_read', this.on_message_read_unread); // event: click on icone 'UnRead' in header - this.$el.on('click', 'a.oe_unread', this.on_message_read_unread); + this.$el.on('click', '.oe_unread', this.on_message_read_unread); // event: click on 'Delete' in msg side menu - this.$el.on('click', 'a.oe_msg_delete', this.on_message_delete); + this.$el.on('click', '.oe_msg_delete', this.on_message_delete); // event: click on 'Reply' in msg - this.$el.on('click', 'a.oe_reply', this.on_message_reply); + this.$el.on('click', '.oe_reply', this.on_message_reply); // event: click on 'Vote' button - this.$el.on('click', 'button.oe_msg_vote', this.on_vote); + this.$el.on('click', '.oe_msg_vote', this.on_vote); // event: click on 'starred/favorite' button - this.$el.on('click', 'a.oe_mail_starbox', this.on_star); + this.$el.on('click', '.oe_mail_starbox', this.on_star); }, + /** + * call the on_compose_message on the thread of this message. + */ on_message_reply:function(event){ event.stopPropagation(); this.thread.on_compose_message(); @@ -659,27 +660,33 @@ openerp.mail = function(session) { }); }, + /** + * instantiate the thread object of this message. + * Each message have only one thread. + */ create_thread: function(){ - var self=this; if(this.thread){ return false; } - /*create thread*/ - self.thread = new mail.Thread(self, self.datasets, { - 'domain': self.domain, + this.thread = new mail.Thread(this, this, { + 'domain': this.domain, 'context':{ - 'default_model': self.datasets.model, - 'default_res_id': self.datasets.res_id, - 'default_parent_id': self.datasets.id + 'default_model': this.model, + 'default_res_id': this.res_id, + 'default_parent_id': this.id }, - 'options': self.options + 'options': this.options } ); /*insert thread in parent message*/ - self.thread.insertAfter(self.$el); + this.thread.insertAfter(this.$el); }, + /** + * Fade out the message and his child thread. + * Then this object is destroyed. + */ animated_destroy: function(options) { var self=this; //graphic effects @@ -687,20 +694,24 @@ openerp.mail = function(session) { self.$el.fadeOut(options.fadeTime, function(){ self.destroy(); }); + self.thread.$el.fadeOut(options.fadeTime); } else { self.destroy(); } }, + /** + * Wait a confirmation for delete the message on the DB. + * Make an animate destroy + */ on_message_delete: function (event) { event.stopPropagation(); if (! confirm(_t("Do you really want to delete this message?"))) { return false; } this.animated_destroy({fadeTime:250}); // delete this message and his childs - var ids = [this.datasets.id].concat( this.get_child_ids() ); + var ids = [this.id].concat( this.get_child_ids() ); this.ds_message.unlink(ids); - this.animated_destroy(); return false; }, @@ -708,9 +719,11 @@ openerp.mail = function(session) { * @param {object} mouse envent */ on_message_read_unread: function (event) { + // TDE note: code here seems complicated... just check that current message is read (value coming from server) + // and send its opposite to set_message_read event.stopPropagation(); // if this message is read, all childs message display is read - var ids = [this.datasets.id].concat( this.get_child_ids() ); + var ids = [this.id].concat( this.get_child_ids() ); var read = $(event.srcElement).hasClass("oe_read"); this.$el.removeClass(read ? 'oe_msg_unread':'oe_msg_read').addClass(read ? 'oe_msg_read':'oe_msg_unread'); @@ -718,12 +731,14 @@ openerp.mail = function(session) { (!read && this.options.typeof_thread == 'archives')) { this.animated_destroy({fadeTime:250}); } - + // TDE note: should have a context here this.ds_notification.call('set_message_read', [ids, read]); return false; }, - /** browse message + /** + * search a message in all thread and child thread. + * This method return an object message. * @param {object}{int} option.id * @param {object}{string} option.model * @param {object}{boolean} option._go_thread_wall @@ -740,7 +755,7 @@ openerp.mail = function(session) { } } - if(this.datasets.id==options.id) + if(this.id==options.id) return this; for(var i in this.thread.messages){ @@ -753,30 +768,36 @@ openerp.mail = function(session) { return false; }, - /* get all child message/thread id linked + /* get all child message id linked. + * @return array of id */ get_child_ids: function(){ var res=[] - if(arguments[0]) res.push(this.datasets.id); + if(arguments[0]) res.push(this.id); if(this.thread){ res = res.concat( this.thread.get_child_ids(true) ); } return res; }, + /** + * add or remove a vote for a message and display the result + */ on_vote: function (event) { event.stopPropagation(); var self=this; - return this.ds_message.call('vote_toggle', [[self.datasets.id]]).pipe(function(vote){ - - self.datasets.has_voted=vote; - self.datasets.vote_nb += self.datasets.has_voted ? 1 : -1; + return this.ds_message.call('vote_toggle', [[self.id]]).pipe(function(vote){ + // TDE note: to update, because vote_user_ids is about to disappear to be replaced by vote_nb (number of votes) + self.has_voted=vote; + self.vote_nb += self.has_voted ? 1 : -1; self.display_vote(); }); return false; }, - // Render vote Display template. + /** + * Display the render of this message's vote + */ display_vote: function () { var self = this; var vote_element = session.web.qweb.render('mail.thread.message.vote', {'widget': self}); @@ -784,14 +805,16 @@ openerp.mail = function(session) { self.$(".oe_mail_vote_count:first").replaceWith(vote_element); }, - // Stared/unstared + Render star. + /** + * add or remove a favorite (or starred) for a message and change class on the DOM + */ on_star: function (event) { event.stopPropagation(); var self=this; var button = self.$('button.oe_mail_starbox:first'); - return this.ds_message.call('favorite_toggle', [[self.datasets.id]]).pipe(function(star){ - self.datasets.is_favorite=star; - if(self.datasets.is_favorite){ + return this.ds_message.call('favorite_toggle', [[self.id]]).pipe(function(star){ + self.is_favorite=star; + if(self.is_favorite){ button.addClass('oe_stared'); } else { button.removeClass('oe_stared'); @@ -805,8 +828,8 @@ openerp.mail = function(session) { }); - /** - * ------------------------------------------------------------ + /** + * ------------------------------------------------------------ * Thread Widget * ------------------------------------------------------------ * @@ -831,10 +854,6 @@ openerp.mail = function(session) { * @param {Object} [options] * @param {Object} [message] read about mail.ThreadMessage object * @param {Object} [thread] - * @param {Number} [expandable_number=5] number message show - * for each click on "show more message" - * @param {Number} [expandable_default_number=5] number message show - * on begin before the first click on "show more message" * @param {int} [display_indented_thread] number thread level to indented threads. * other are on flat mode * @param {Select} [typeof_thread] inbox/archives/stared/sent @@ -851,42 +870,25 @@ openerp.mail = function(session) { default_res_id: 0, default_parent_id: false }, options.context || {}); - // options - this.options = _.extend(options.options, { - 'expandable_number': options.options.expandable_number || 5, - 'expandable_default_number': options.options.expandable_default_number || 5, - '_expandable_max': options.options.expandable_default_number || 5, - 'display_indented_thread': options.options.display_indented_thread || -1, - 'show_reply_button': options.options.show_reply_button || -1, - 'typeof_thread': options.options.typeof_thread || 'inbox', - 'show_header_compose': options.options.show_header_compose || false, - '_parents': (options.options._parents != undefined ? options.options._parents : []).concat( [this] ) - }); + this.options = options.options; + this.options._parents = (options.options._parents != undefined ? options.options._parents : []).concat( [this] ); // record options and data this.parent_message= parent.thread!= undefined ? parent : false ; - // datasets and internal vars - this.datasets = { - 'id' : datasets.id || false, - 'model' : datasets.model || false, - 'parent_id' : datasets.parent_id || false, - 'is_private' : datasets.is_private || false, - 'author_id' : datasets.author_id || false, - 'thread_level' : (datasets.thread_level+1) || 0, - 'partner_ids' : [] - }; - - this.datasets.show_composeform = this.options.show_header_compose && this.options.show_reply_button>this.datasets.thread_level; - - for(var i in datasets.partner_ids){ - if(datasets.partner_ids[i][0]!=(datasets.author_id ? datasets.author_id[0] : -1)){ - this.datasets.partner_ids.push(datasets.partner_ids[i]); - } - } - + // data of this thread + this.id = datasets.id || false, + this.model = datasets.model || false, + this.parent_id = datasets.parent_id || false, + this.is_private = datasets.is_private || false, + this.author_id = datasets.author_id || false, + this.thread_level = (datasets.thread_level+1) || 0, + this.partner_ids = _.filter(datasets.partner_ids, function(partner){ return partner[0]!=datasets.author_id[0]; } ) this.messages = []; - this.composeMessage = false; + this.show_compose_message = this.options.show_compose_message && this.options.show_reply_button > this.thread_level; + + // object compose message + this.ComposeMessage = false; this.ds_thread = new session.web.DataSetSearch(this, this.context.default_model || 'mail.thread'); this.ds_message = new session.web.DataSetSearch(this, 'mail.message'); @@ -894,26 +896,29 @@ openerp.mail = function(session) { start: function() { this._super.apply(this, arguments); - if(this.datasets.show_composeform){ + if(this.show_compose_message){ this.instantiate_ComposeMessage(); } this.bind_events(); }, + /* instantiate the compose message object and insert this on the DOM. + * The compose message is display in compact form. + */ instantiate_ComposeMessage: function(){ // add message composition form view - this.composeMessage = new mail.ThreadComposeMessage(this, this.datasets, { + this.ComposeMessage = new mail.ThreadComposeMessage(this, this, { 'context': this.context, 'options': this.options, }); - if(this.datasets.thread_level){ - this.composeMessage.appendTo(this.$el); + if(this.thread_level){ + this.ComposeMessage.appendTo(this.$el); } else { // root view - this.composeMessage.prependTo(this.$el); + this.ComposeMessage.prependTo(this.$el); } - this.composeMessage.do_hide_compact(); + this.ComposeMessage.do_hide_compact(); }, /* When the expandable object is visible on screen (with scrolling) @@ -921,17 +926,18 @@ openerp.mail = function(session) { */ on_scroll: function(event){ if(event)event.stopPropagation(); - var message = this.messages[0]; - if(message && message.datasets.type=="expandable" && message.datasets.max_limit){ + this.$('.oe_msg_expandable:last'); + + var message = this.messages[this.messages.length-1]; + if(message && message.type=="expandable" && message.max_limit){ var pos = message.$el.position(); if(pos.top){ /* bottom of the screen */ var bottom = $(window).scrollTop()+$(window).height()+200; - if(bottom - pos.top > 0){ + if(bottom > pos.top){ message.on_expandable(); } } - } }, @@ -944,27 +950,36 @@ openerp.mail = function(session) { self.$el.on('click', '.oe_mail_compose_textarea .oe_more_hidden', self.on_hide_recipients); }, + /** + *show all the partner list of this parent message + */ on_show_recipients: function(){ var p=$(this).parent(); p.find('.oe_more_hidden, .oe_hidden').show(); p.find('.oe_more').hide(); }, + /** + *hide a part of the partner list of this parent message + */ on_hide_recipients: function(){ var p=$(this).parent(); p.find('.oe_more_hidden, .oe_hidden').hide(); p.find('.oe_more').show(); }, - /* get all child message/thread id linked + /* get all child message/thread id linked. + * @return array of id */ get_child_ids: function(){ var res=[]; - _(this.get_childs()).each(function (val, key) { res.push(val.datasets.id); }); + _(this.get_childs()).each(function (val, key) { res.push(val.id); }); return res; }, - /* get all child message/thread linked + /* get all child message/thread linked. + * @param {int} nb_thread_level, number of traversed thread level for this search + * @return array of thread object */ get_childs: function(nb_thread_level){ var res=[]; @@ -979,7 +994,9 @@ openerp.mail = function(session) { return res; }, - /** browse thread + /** + *search a thread in all thread and child thread. + * This method return an object thread. * @param {object}{int} option.id * @param {object}{string} option.model * @param {object}{boolean} option._go_thread_wall @@ -995,7 +1012,7 @@ openerp.mail = function(session) { return this.options._parents[0].browse_thread(options); } - if(this.datasets.id==options.id){ + if(this.id==options.id){ return this; } @@ -1015,54 +1032,66 @@ openerp.mail = function(session) { return false; }, - /** browse message + /** + *search a message in all thread and child thread. + * This method return an object message. * @param {object}{int} option.id * @param {object}{string} option.model * @param {object}{boolean} option._go_thread_wall * private for check the top thread - * @return thread object + * @return message object */ browse_message: function(options){ if(this.options._parents[0].messages[0]) return this.options._parents[0].messages[0].browse_message(options); }, - /* this function is launch when a user click on "Reply" button + /** + *If ComposeMessage doesn't exist, instantiate the compose message. + * Call the on_compose_expandable method to allow the user to write his message. + * (Is call when a user click on "Reply" button) */ on_compose_message: function(){ - this.composeMessage.on_compose_expandable(); + if(!this.ComposeMessage){ + this.instantiate_ComposeMessage(); + this.ComposeMessage.do_hide_compact(); + } + + this.ComposeMessage.on_compose_expandable(); }, - /* display the no message on the thread + /** + *display the message "there are no message" on the thread */ no_message: function(){ $(session.web.qweb.render('mail.wall_no_message', {})).appendTo(this.$el); }, - /** 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 + /** + *make a request to read the message (calling RPC to "message_read"). + * The result of this method is send to the switch message for sending ach message to + * his parented object thread. * @param {Array} replace_domain: added to this.domain * @param {Object} replace_context: added to this.context + * @param {Array} ids read (if the are some ids, the method don't use the domain) */ - message_fetch: function (initial_mode, replace_domain, replace_context, ids, callback) { + message_fetch: function (replace_domain, replace_context, ids) { var self = this; - // initial mode: try to use message_data or message_ids - if (initial_mode && this.options.message_data) { - return this.create_message_object(this.options.message_data); - } // domain and context: options + additional fetch_domain = replace_domain ? replace_domain : this.domain; fetch_context = replace_context ? replace_context : this.context; - var message_loaded = [this.datasets.id||0].concat( self.options._parents[0].get_child_ids() ); + var message_loaded = [this.id||0].concat( self.options._parents[0].get_child_ids() ); return this.ds_message.call('message_read', [ids, fetch_domain, message_loaded, fetch_context, this.context.default_parent_id || undefined] ).then(this.proxy('switch_new_message')); }, - /* create record object and linked him + /** + *create the message object and attached on this thread. + * When the message object is create, this method call insert_message for, + * displaying this message on the DOM. + * @param : {object} data from calling RPC to "message_read" */ create_message_object: function (data) { var self = this; @@ -1073,10 +1102,10 @@ openerp.mail = function(session) { 'context': { 'default_model': data.model || self.context.default_model, 'default_res_id': data.res_id || self.context.default_res_id, - 'default_parent_id': self.datasets.id }, + 'default_parent_id': self.id }, }); } else { - var message = new mail.ThreadMessage(self, _.extend(data, {'thread_level': self.datasets.thread_level}), { + var message = new mail.ThreadMessage(self, _.extend(data, {'thread_level': self.thread_level}), { 'domain': data.domain, 'context': { 'default_model': data.model, @@ -1086,23 +1115,39 @@ openerp.mail = function(session) { }); } + // insert the message on dom + self.insert_message( message ); + // check if the message is already create for(var i in self.messages){ - if(self.messages[i].datasets.id == message.datasets.id){ + if(self.messages[i].id == message.id){ self.messages[i].destroy(); - self.messages[i] = self.insert_message(message); - return true; + if(message.id>0){ + self.messages[i] = message; + return true; + } else { + delete self.messages[i]; + } } } - self.messages.push( self.insert_message(message) ); + self.messages.push( message ); }, - /** Displays a message or an expandable message */ + /** + *insert the message on the DOM. + * All message (and expandable message) are sorted. The method get the + * older and newer message to insert the message (before, after). + * If there are no older or newer, the message is prepend or append to + * the thread (parent object or on root thread for flat view). + * The sort is define by the thread_level (O for newer on top). + * @param : {object} ThreadMessage object + */ insert_message: function (message) { var self=this; - if(this.datasets.show_composeform){ - this.composeMessage.do_show_compact(); + if(this.show_compose_message && + this.options.display_indented_thread > self.thread_level){ + this.ComposeMessage.do_show_compact(); } this.$('.oe_wall_no_message').remove(); @@ -1110,32 +1155,32 @@ openerp.mail = function(session) { // insert on hierarchy display => insert in self child var thread_messages = self.messages; var thread = self; - if( self.options.display_indented_thread < self.datasets.thread_level ) { + if( self.options.display_indented_thread < self.thread_level ) { var thread = self.options._parents[self.options.display_indented_thread] || self.options._parents[0]; var thread_messages = []; _(thread.get_childs()).each(function (val, key) { thread_messages.push(val.parent_message); }); } - // check older and newer message for insert + // check older and newer message for insertion var parent_newer = false; var parent_older = false; - for(var i in thread_messages){ - if((thread_messages[i].datasets.id > message.datasets.id && message.datasets.id > 0) || - thread_messages[i].datasets.id < 0){ - if(!parent_newer || - thread_messages[i].datasets.id < 0 || - parent_newer.datasets.id >= thread_messages[i].datasets.id) { - parent_newer = thread_messages[i]; - } - } else if((thread_messages[i].datasets.id > 0 && thread_messages[i].datasets.id < message.datasets.id) || message.datasets.id < 0) { - if(!parent_older || - parent_older.datasets.id < thread_messages[i].datasets.id) { - parent_older = thread_messages[i]; + if(message.id > 0){ + for(var i in thread_messages){ + if(thread_messages[i].id > message.id){ + if(!parent_newer || + parent_newer.id > thread_messages[i].id) { + parent_newer = thread_messages[i]; + } + } else if(thread_messages[i].id > 0 && thread_messages[i].id < message.id) { + if(!parent_older || + parent_older.id < thread_messages[i].id) { + parent_older = thread_messages[i]; + } } } } - var sort = self.datasets.thread_level; + var sort = (!!self.thread_level || message.id<0); if(parent_older){ if(sort){ @@ -1144,13 +1189,13 @@ openerp.mail = function(session) { message.insertBefore(parent_older.$el); } } else if(parent_newer){ - if(sort || parent_newer.datasets.id<0){ + if(sort || parent_newer.id<0){ message.insertBefore(parent_newer.$el); } else { message.insertAfter(parent_newer.thread ? parent_newer.thread.$el : parent_newer.$el); } } else { - if(sort) { + if(sort && message.id>0) { message.prependTo(thread.$el); } else { message.appendTo(thread.$el); @@ -1160,7 +1205,11 @@ openerp.mail = function(session) { return message }, - /* Send the records to his parent thread */ + /** + *get the parent thread of the messages. + * Each message is send to his parent object for creating the object message. + * @param : {Array} datas from calling RPC to "message_read" + */ switch_new_message: function(records) { var self=this; _(records).each(function(record){ @@ -1172,8 +1221,8 @@ openerp.mail = function(session) { }, }); - /** - * ------------------------------------------------------------ + /** + * ------------------------------------------------------------ * mail : root Widget * ------------------------------------------------------------ * @@ -1202,13 +1251,37 @@ openerp.mail = function(session) { *... @param {int} [show_read_unread_button] number thread level to display the read/unread button *... @param {int} [display_indented_thread] number thread level to indented threads. * other are on flat mode + *... @param {Boolean} [show_compose_message] allow to display the composer + *... @param {Boolean} [show_compact_message] display the compact message on the thread + * when the user clic on this compact mode, the composer is open */ init: function (parent, options) { this._super(parent); - this.options = options || {}; - this.options.domain = options.domain || []; - this.options.context = options.context || {}; + this.domain = options.domain || []; + this.context = options.context || {}; this.search_results = {'domain': [], 'context': {}, 'groupby': {}}; + + this.options = _.extend({ + 'typeof_thread' : 'inbox', + 'display_indented_thread' : -1, + 'show_reply_button' : -1, + 'show_read_unread_button' : -1, + 'truncate_limit' : 250, + 'show_record_name' : false, + 'show_compose_message' : false, + 'show_compact_message' : false + }, options); + + if(this.display_indented_thread === false) { + this.display_indented_thread = -1; + } + if(this.show_reply_button === false) { + this.show_reply_button = -1; + } + if(this.show_read_unread_button === false) { + this.show_read_unread_button = -1; + } + }, start: function (options) { @@ -1217,31 +1290,31 @@ openerp.mail = function(session) { this.bind_events(); }, + /** - * Display the threads - */ + *Create the root thread and display this object in the DOM. + * Call the no_message method then c all the message_fetch method + * of this root thread to display the messages. + */ message_render: function (search) { + this.thread = new mail.Thread(this, {}, { - 'domain' : this.options.domain, - 'context' : this.options.context, - 'options': { - 'typeof_thread': this.options.typeof_thread || 'inbox', - 'display_indented_thread': this.options.display_indented_thread || 1, - 'show_reply_button': this.options.show_reply_button || 1, - 'show_read_unread_button': this.options.show_read_unread_button || 1, - 'truncate_limit': this.options.truncate_limit || 250, - 'show_record_name': this.options.show_record_name || false, - 'show_header_compose': this.options.show_header_compose || false - }, + 'domain' : this.domain, + 'context' : this.context, + 'options': this.options, }); this.thread.appendTo( this.$el ); this.thread.no_message(); this.thread.message_fetch(); + + if(this.options.show_compose_message){ + this.thread.ComposeMessage.do_show_compact(); + } }, bind_events: function(){ - if(this.options.context['typeof_thread']!='other'){ + if(this.context['typeof_thread']!='other'){ $(document).scroll( this.thread.on_scroll ); $(window).resize( this.thread.on_scroll ); window.setTimeout( this.thread.on_scroll, 500 ); @@ -1250,8 +1323,8 @@ openerp.mail = function(session) { }); - /** - * ------------------------------------------------------------ + /** + * ------------------------------------------------------------ * mail_thread Widget * ------------------------------------------------------------ * @@ -1280,6 +1353,7 @@ openerp.mail = function(session) { this.$el.toggle(this.view.get("actual_mode") !== "create"); }, render_value: function() { + var self = this; if (! this.view.datarecord.id || session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) { this.$('oe_mail_thread').hide(); return; @@ -1293,7 +1367,7 @@ openerp.mail = function(session) { var domain = this.options.domain.concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]); // TDE note: replace message_is_follower by a check in message_follower_ids, as message_is_follower is not used in views anymore - var show_header_compose = this.view.is_action_enabled('edit') || + var show_compose_message = this.view.is_action_enabled('edit') || (this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value()); if(this.root){ @@ -1304,10 +1378,10 @@ openerp.mail = function(session) { 'domain' : domain, 'context' : this.options.context, 'typeof_thread': this.options.context['typeof_thread'] || 'other', - 'display_indented_thread': false, - 'show_reply_button': false, - 'show_read_unread_button': false, - 'show_header_compose': show_header_compose + 'display_indented_thread': -1, + 'show_reply_button': 10, + 'show_read_unread_button': -1, + 'show_compose_message': show_compose_message } ); @@ -1316,8 +1390,8 @@ openerp.mail = function(session) { }); - /** - * ------------------------------------------------------------ + /** + * ------------------------------------------------------------ * Wall Widget * ------------------------------------------------------------ * @@ -1392,7 +1466,7 @@ openerp.mail = function(session) { /** - * Display the threads + *Create the root thread widget and display this object in the DOM */ message_render: function (search) { var domain = this.options.domain.concat(this.search_results['domain']); @@ -1402,24 +1476,26 @@ openerp.mail = function(session) { 'context' : context, 'typeof_thread': context['typeof_thread'] || 'other', 'display_indented_thread': 2, - 'show_reply_button': 2, - 'show_read_unread_button': 3, - 'show_header_compose': true + 'show_reply_button': 10, + 'show_read_unread_button': 11, + 'show_compose_message': true, + 'show_compact_message': true } ); + return this.root.appendTo( this.$('.oe_mail_wall_threads:first') ); }, bind_events: function(){ var self=this; - this.$("button.oe_write_full:first").click(function(){ self.root.thread.composeMessage.on_compose_fullmail(); }); + this.$("button.oe_write_full:first").click(function(){ self.root.thread.ComposeMessage.on_compose_fullmail(); }); this.$("button.oe_write_onwall:first").click(function(){ self.root.thread.on_compose_message(); }); } }); /** - * ------------------------------------------------------------ + * ------------------------------------------------------------ * UserMenu * ------------------------------------------------------------ * diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js index 735b3e00e0b..f08991be2cc 100644 --- a/addons/mail/static/src/js/mail_followers.js +++ b/addons/mail/static/src/js/mail_followers.js @@ -39,6 +39,7 @@ openerp_mail_followers = function(session, mail) { this._check_visibility(); this.reinit(); this.bind_events(); + this._super(); }, _check_visibility: function() { @@ -72,7 +73,7 @@ openerp_mail_followers = function(session, mail) { target: 'new', context: { 'default_res_model': self.view.dataset.model, - 'default_res_id': self.view.dataset.ids[0] + 'default_res_id': self.view.datarecord.id, }, } self.do_action(action, { @@ -85,18 +86,11 @@ openerp_mail_followers = function(session, mail) { read_value: function () { var self = this; - return this.ds_model.read_ids([this.view.dataset.ids[0]], ['message_follower_ids']).pipe(function (results) { + return this.ds_model.read_ids([this.view.datarecord.id], ['message_follower_ids']).pipe(function (results) { self.set_value(results[0].message_follower_ids); }); }, - set_value: function (value_) { - this._super(value_); - // TDE FIXME: render_value is never called... ask to niv - // TDE TODO: in start, call this._super(), should resolve this issue - this.render_value(); - }, - render_value: function () { this.reinit(); return this.fetch_followers(this.get("value")); @@ -188,8 +182,7 @@ openerp_mail_followers = function(session, mail) { display_subtypes:function (data) { var self = this; var subtype_list_ul = this.$('.oe_subtypes'); - var records = data[this.view.dataset.ids[0]].message_subtype_data; - + var records = data[this.view.datarecord.id || this.view.dataset.ids[0]].message_subtype_data; _(records).each(function (record, record_name) { record.name = record_name; record.followed = record.followed || undefined; @@ -209,7 +202,7 @@ openerp_mail_followers = function(session, mail) { $(record).attr('checked',false); }); var context = new session.web.CompoundContext(this.build_context(), {}); - return this.ds_model.call('message_unsubscribe_users', [[this.view.dataset.ids[0]], [this.session.uid], context]).pipe(this.proxy('read_value')); + return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context]).pipe(this.proxy('read_value')); }, do_update_subscription: function (event) { diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 614a3b5c325..1832b235170 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -17,7 +17,7 @@
- User img + User img
@@ -70,8 +70,8 @@ Template used to display attachments in a mail.message --> -
    - +
      +
    • @@ -82,7 +82,7 @@ - + x @@ -105,13 +105,13 @@
      Post to: - All Followers - and + All Followers + and - + - - , others... + + , others... <<<
      @@ -174,9 +174,9 @@ -->
      -
        +
        -
      +
      @@ -185,43 +185,42 @@ -
      - +
      - W - W - ( - 7 + W + W + ( + 7
      -

      - +

      +

      - - + + - +
      - +

      @@ -231,9 +230,9 @@ -
      + @@ -254,22 +253,22 @@ diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index d85988a6c90..2eff59fe0ed 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -581,11 +581,19 @@ class test_mail(TestMailMockups): reply_msg = MAIL_TEMPLATE.format(to='Pretty Pigs , other@gmail.com', subject='Re: 1', extra='In-Reply-To: %s' % msg1.message_id) self.mail_group.message_process(cr, uid, None, reply_msg) + # TDE note: temp various asserts because of the random bug about msg1.child_ids + msg_ids = self.mail_message.search(cr, uid, [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], limit=1) + new_msg = self.mail_message.browse(cr, uid, msg_ids[0]) + self.assertEqual(new_msg.parent_id, msg1, 'Newly processed mail_message (%d) should have msg1 as parent' % (new_msg.id)) # 2. References header reply_msg2 = MAIL_TEMPLATE.format(to='Pretty Pigs , other@gmail.com', subject='Re: Re: 1', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % msg1.message_id) self.mail_group.message_process(cr, uid, None, reply_msg2) + # TDE note: temp various asserts because of the random bug about msg1.child_ids + msg_ids = self.mail_message.search(cr, uid, [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], limit=1) + new_msg = self.mail_message.browse(cr, uid, msg_ids[0]) + self.assertEqual(new_msg.parent_id, msg1, 'Newly processed mail_message should have msg1 as parent') # 3. Subject contains [] + model passed to message+process -> only attached to group, not to mail reply_msg3 = MAIL_TEMPLATE.format(to='Pretty Pigs , other@gmail.com', @@ -595,6 +603,12 @@ class test_mail(TestMailMockups): group_pigs.refresh() msg1.refresh() self.assertEqual(5, len(group_pigs.message_ids), 'group should contain 5 messages') + # TDE note: python test + debug because of the random error we see with the next assert + if len(msg1.child_ids) != 2: + msg_ids = self.mail_message.search(cr, uid, [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], limit=10) + for new_msg in self.mail_message.browse(cr, uid, msg_ids): + print new_msg.subject, '(id', new_msg.id, ')', 'parent_id:', new_msg.parent_id + print '\tchild_ids', [child.id for child in new_msg.child_ids] self.assertEqual(2, len(msg1.child_ids), 'msg1 should have 2 children now') def test_60_message_vote(self): diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml index ce86e0bacfd..255954e31c1 100644 --- a/addons/mail/wizard/mail_compose_message_view.xml +++ b/addons/mail/wizard/mail_compose_message_view.xml @@ -6,14 +6,14 @@ mail.compose.message - - - - - - - + + + + + + + /> \n" +"PO-Revision-Date: 2012-10-19 17:22+0000\n" +"Last-Translator: Herczeg Péter \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-19 05:11+0000\n" +"X-Launchpad-Export-Date: 2012-10-20 05:07+0000\n" "X-Generator: Launchpad (build 16165)\n" #. module: sale @@ -400,7 +399,7 @@ msgstr "Rendelés" #. module: sale #: field:sale.order,message_ids:0 msgid "Messages" -msgstr "" +msgstr "Üzenetek" #. module: sale #: selection:sale.report,month:0 @@ -634,7 +633,7 @@ msgstr "Megerősítés" #. module: sale #: view:sale.order:0 msgid "Unread messages" -msgstr "" +msgstr "Olvasatlan üzenetek" #. module: sale #: field:sale.order,partner_shipping_id:0 @@ -672,7 +671,7 @@ msgstr "" #. module: sale #: field:sale.order,message_unread:0 msgid "Unread Messages" -msgstr "" +msgstr "Olvasatlan üzenetek" #. module: sale #: view:sale.order:0 @@ -1105,7 +1104,7 @@ msgstr "" #. module: sale #: model:ir.model,name:sale.model_mail_message msgid "Message" -msgstr "" +msgstr "Üzenet" #. module: sale #: field:sale.config.settings,module_warning:0 @@ -1323,7 +1322,7 @@ msgstr "" #. module: sale #: field:sale.order,message_is_follower:0 msgid "Is a Follower" -msgstr "" +msgstr "követő" #. module: sale #: code:addons/sale/sale.py:261 @@ -1638,7 +1637,7 @@ msgstr "Mégsem" #. module: sale #: field:sale.order,message_follower_ids:0 msgid "Followers" -msgstr "" +msgstr "Követők" #. module: sale #: code:addons/sale/sale.py:947 @@ -2012,7 +2011,7 @@ msgstr "" #. module: sale #: help:sale.order,message_ids:0 msgid "Messages and communication history" -msgstr "" +msgstr "Üzenetek és kommunikáció történet" #. module: sale #: view:sale.order:0 diff --git a/addons/stock/security/stock_security.xml b/addons/stock/security/stock_security.xml index c9f8a03a378..f052c458632 100644 --- a/addons/stock/security/stock_security.xml +++ b/addons/stock/security/stock_security.xml @@ -5,6 +5,7 @@ User + Manager