+
diff --git a/addons/mail/security/ir.model.access.csv b/addons/mail/security/ir.model.access.csv
index 011e8b27dc5..1fb909f4435 100644
--- a/addons/mail/security/ir.model.access.csv
+++ b/addons/mail/security/ir.model.access.csv
@@ -1,7 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_mail_message,mail.message,model_mail_message,,1,0,1,0
-access_mail_message_all,mail.message.all,model_mail_message,base.group_user,1,1,1,1
+access_mail_message_all,mail.message.all,model_mail_message,,1,0,0,0
+access_mail_message_group_user,mail.message.group.user,model_mail_message,base.group_user,1,1,1,1
access_mail_thread,mail.thread,model_mail_thread,base.group_user,1,1,1,0
-access_mail_subscription,mail.subscription,model_mail_subscription,,1,1,1,1
-access_mail_notification,mail.notification,model_mail_notification,,1,1,1,1
+access_mail_subscription_all,mail.subscription.all,model_mail_subscription,,1,1,1,1
+access_mail_notification_all,mail.notification.all,model_mail_notification,,1,1,1,1
access_mail_group,mail.group,model_mail_group,base.group_user,1,1,1,1
diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css
index 0cf932f9a25..03f1078e6d5 100644
--- a/addons/mail/static/src/css/mail.css
+++ b/addons/mail/static/src/css/mail.css
@@ -1,7 +1,6 @@
-
-/* ------------------------------ */
-/* Wall */
-/* ------------------------------ */
+/* ------------------------------------------------------------ */
+/* Wall
+/* ------------------------------------------------------------ */
.openerp div.oe_mail_wall {
overflow: auto;
@@ -9,6 +8,17 @@
background: white;
}
+.openerp div.oe_mail_wall_main {
+ float: left;
+ width: 560px;
+ margin: 8px;
+}
+
+.openerp div.oe_mail_wall_aside {
+ margin-left: 565px;
+ margin: 8px;
+}
+
.openerp div.oe_mail_wall_action {
padding: 8px;
background: #eee;
@@ -21,36 +31,32 @@
clear: both;
}
-.openerp .oe_mail_wall_action textarea {
+.openerp div.oe_mail_wall_action .oe_mail_msg_content {
+ width: 484px;
+}
+
+.openerp div.oe_mail_wall_action textarea.oe_mail_compose_textarea,
+.openerp div.oe_mail_wall_action div.oe_mail_compose_message_body_text textarea {
width: 474px;
height: 60px;
padding: 4px;
- margin-bottom: 8px;
- float: right;
+ margin-bottom: 2px;
}
-/* 2 columns view */
-.openerp div.oe_mail_wall_main {
- float: left;
- width: 560px;
- margin: 8px;
-}
-
-.openerp div.oe_mail_wall_aside {
- margin-left: 565px;
- margin: 8px;
-}
-
-/* Threads */
-.openerp .oe_mail_wall_threads {
+.openerp ul.oe_mail_wall_threads {
margin-top: 8px;
}
-.openerp .oe_mail_wall_threads textarea {
- height: 40px;
+
+/* Specific display of threads in the wall */
+/* ------------------------------------------------------------ */
+
+.openerp ul.oe_mail_wall_threads .oe_mail_msg_content textarea {
+ width: 434px;
+ height: 30px;
padding: 4px;
}
-.openerp .oe_mail_wall_thread:first .oe_mail_msg_notification {
+.openerp li.oe_mail_wall_thread:first .oe_mail_msg_notification {
border-top: 0;
}
@@ -64,7 +70,7 @@
width: 486px;
}
-.openerp div.oe_mail_msg_content li{
+.openerp div.oe_mail_msg_content li {
float: left;
margin-right: 3px;
}
@@ -79,40 +85,28 @@
}
-/* ------------------------------ */
-/* RecordThread */
-/* ------------------------------ */
+/* ------------------------------------------------------------ */
+/* RecordThread
+/* ------------------------------------------------------------ */
.openerp div.oe_mail_recthread {
overflow: auto;
}
-/* Left-side CSS */
-.openerp div.oe_mail_recthread_actions {
- margin-bottom: 8px;
-}
-
-.openerp div.oe_mail_recthread_followers {
- margin-bottom: 8px;
-}
-
-.openerp div.oe_mail_recthread_followers img.oe_mail_msg_image {
- width: 28px;
- height: 28px;
- margin: 4px;
-}
-
-/* RecordThread: 2 columns view */
-.openerp div.oe_mail_recthread_left {
+.openerp div.oe_mail_recthread_main {
float: left;
width: 560px;
}
-.openerp div.oe_mail_recthread_right {
+.openerp div.oe_mail_recthread_aside {
float: right;
width: 250px;
}
+.openerp div.oe_mail_recthread_actions {
+ margin-bottom: 8px;
+}
+
.openerp div.oe_mail_recthread_actions button {
width: 120px;
}
@@ -144,16 +138,17 @@
background-image: linear-gradient(to bottom, #dc5f59, #b33630);
}
-.openerp textarea.oe_mail_action_textarea {
- height: 60px;
- padding: 5px;
+.openerp div.oe_mail_recthread_followers {
+ margin-bottom: 8px;
}
-/* ------------------------------ */
-/* ThreadDisplay */
-/* ------------------------------ */
+
+/* ------------------------------------------------------------ */
+/* Thread
+/* ------------------------------------------------------------ */
.openerp div.oe_mail_thread_action {
+ display: none;
white-space: normal;
padding: 8px;
background: #eee;
@@ -166,6 +161,26 @@
clear: both;
}
+/* default textarea (oe_mail_compose_textarea), and body_text textarea for compose form view */
+.openerp .oe_mail_msg_content textarea.oe_mail_compose_textarea,
+.openerp .oe_mail_msg_content div.oe_mail_compose_message_body_text textarea {
+ width: 474px;
+ height: 60px;
+ padding: 4px;
+ font-size: 12px;
+ border: 1px solid #cccccc;
+}
+
+/* default textarea (oe_mail_compose_textarea), and body_text textarea for compose form view */
+.openerp .oe_mail_msg_content textarea.oe_mail_compose_textarea:focus,
+.openerp .oe_mail_msg_content div.oe_mail_compose_message_body_text textarea:focus {
+ outline: 0;
+ border-color: rgba(82, 168, 236, 0.8);
+ -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
+ -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
+ -box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
+}
+
.openerp div.oe_mail_thread_display {
white-space: normal;
}
@@ -174,7 +189,7 @@
margin-left: 66px;
}
-.openerp div.oe_mail_thread_subthread .oe_mail_thread_msg:last-child {
+.openerp div.oe_mail_thread_subthread li.oe_mail_thread_msg:last-child {
margin-bottom: 8px;
}
@@ -183,18 +198,23 @@
border-bottom: 1px solid #D2D9E7;
}
-.openerp .oe_mail_thread_msg:after {
+.openerp li.oe_mail_thread_msg:after {
content: "";
display: block;
clear: both;
}
-.openerp .oe_mail_thread_msg > div:after {
+.openerp li.oe_mail_thread_msg > div:after {
content: "";
display: block;
clear: both;
}
+.openerp div.oe_mail_msg {
+ padding: 0;
+ margin: 0 0 4px 0;
+}
+
.openerp .oe_mail_msg_notification,
.openerp .oe_mail_msg_comment,
.openerp .oe_mail_msg_email {
@@ -203,11 +223,6 @@
border-top: 1px solid #ccc;
}
-.openerp .oe_email_icon {
- width: 50px;
- height: 50px;
-}
-
.openerp div.oe_mail_thread_subthread .oe_mail_msg_comment {
background: #eee;
}
@@ -230,9 +245,18 @@
clear: both;
}
-.openerp img.oe_mail_msg_image {
+.openerp img.oe_mail_icon {
width: 50px;
height: 50px;
+}
+
+.openerp img.oe_mail_thumbnail {
+ width: 28px;
+ height: 28px;
+ margin: 4px;
+}
+
+.openerp img.oe_mail_frame {
text-align: center;
overflow: hidden;
-moz-border-radius: 3px;
@@ -247,48 +271,60 @@
clip: rect(5px, 40px, 45px, 0px);
}
-/* ------------------------------ */
-/* Styling (should be openerp) */
-/* ------------------------------ */
+.openerp .oe_mail_invisible {
+ display: none;
+}
-.openerp input.oe_mail, textarea.oe_mail {
- width: 432px;
- padding: 4px;
- font-size: 12px;
+/* ------------------------------------------------------------ */
+/* mail.compose.message form view & OpenERP hacks
+/* ------------------------------------------------------------ */
+
+/* form_view: delete white background */
+.openerp .oe_mail_msg_content div.oe_formview {
+ background-color: transparent;
+}
+
+.openerp .oe_mail_msg_content div.oe_form_nosheet {
+ margin: 0px;
+}
+
+.openerp .oe_mail_msg_content table.oe_form_group {
+ margin: 0px;
+}
+
+.openerp .oe_mail_msg_content table.oe_form_field,
+.openerp .oe_mail_msg_content div.oe_form_field {
+ padding: 0px;
+}
+
+.openerp .oe_mail_msg_content td.oe_form_group_cell {
+ vertical-align: bottom;
+}
+
+/* subject: change width */
+.openerp .oe_mail_msg_content .oe_form .oe_form_field input {
+ width: 472px;
+}
+
+/* body_html: cleditor */
+.openerp .oe_mail_msg_content div.cleditorMain {
border: 1px solid #cccccc;
- -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
- -moz-transition: border linear 0.2s, box-shadow linear 0.2s;
- -ms-transition: border linear 0.2s, box-shadow linear 0.2s;
- -o-transition: border linear 0.2s, box-shadow linear 0.2s;
- transition: border linear 0.2s, box-shadow linear 0.2s;
- -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
- -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
- -box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
}
-.openerp input.oe_mail:focus, textarea.oe_mail:focus {
- outline: 0;
- border-color: rgba(82, 168, 236, 0.8);
- -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
- -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
- -box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
+/* destination_partner_ids */
+.openerp .oe_mail_msg_content div.text-core {
+ height: 22px !important;
+ width: 472px;
}
-.openerp div.oe_mail_msg {
- padding: 0;
- margin: 0 0 4px 0;
+/* buttons */
+.openerp .oe_mail_msg_content .oe_mail_compose_message_icons button.oe_form_button {
+ padding: 1px;
}
-.openerp .oe_mail_oe_bold {
- font-weight: bold;
-}
-
-/* ------------------------------ */
-/* Messages layout */
-/* ------------------------------ */
+/* ------------------------------------------------------------ */
+/* Messages layout
+/* ------------------------------------------------------------ */
.openerp .oe_mail_msg .oe_mail_msg_title {
margin: 0;
@@ -318,6 +354,10 @@
display: inline;
}
+.openerp .oe_mail_oe_bold {
+ font-weight: bold;
+}
+
/* Read more/less link */
.openerp .oe_mail_msg_content .expand,
.openerp .oe_mail_msg_content .reduce {
@@ -360,35 +400,3 @@
padding: 0;
list-style-type: square;
}
-
-
-/* ------------------------------ */
-/* Group Form */
-/* ------------------------------ */
-
-div.oe_mail_group_advanced_details {
- display: none;
-}
-
-.oe_form_sheetbg.openerp_mail_group_sheet {
- min-height: 0px;
- max-height: none;
-}
-
-.oe_form_sheetbg.openerp_mail_group_sheet .oe_form_sheet {
- min-height: 0px;
- max-height: none;
- padding: 0px 18px;
- max-width: 80%;
-}
-
-/* Resize group logo */
-.oe_form_sheetbg.openerp_mail_group_sheet .oe_form_field_image > img {
- max-width: 100px;
- max-height: 100px;
-}
-
-/* Resize group description */
-.oe_form_sheetbg.openerp_mail_group_sheet .oe_form_field_text > textarea {
- height: 40px;
-}
diff --git a/addons/mail/static/src/css/mail_compose_message.css b/addons/mail/static/src/css/mail_compose_message.css
new file mode 100644
index 00000000000..4cd1339ded4
--- /dev/null
+++ b/addons/mail/static/src/css/mail_compose_message.css
@@ -0,0 +1,16 @@
+/* ------------------------------ */
+/* Compose Message Wizard Form */
+/* ------------------------------ */
+
+.openerp tr td .oe_form_field.oe_mail_compose_message_invisible {
+ display: none;
+}
+
+.openerp .oe_mail_compose_message_icons {
+ text-align: right;
+}
+
+.openerp .oe_mail_compose_message_icons img {
+ width: 20px;
+ height: 20px;
+}
diff --git a/addons/mail/static/src/img/_fp.png b/addons/mail/static/src/img/_fp.png
deleted file mode 100644
index 17dce2a047c..00000000000
Binary files a/addons/mail/static/src/img/_fp.png and /dev/null differ
diff --git a/addons/mail/static/src/img/attachment.png b/addons/mail/static/src/img/attachment.png
new file mode 100644
index 00000000000..5cc0c33257d
Binary files /dev/null and b/addons/mail/static/src/img/attachment.png differ
diff --git a/addons/mail/static/src/img/checklist.png b/addons/mail/static/src/img/checklist.png
new file mode 100644
index 00000000000..d252606ff53
Binary files /dev/null and b/addons/mail/static/src/img/checklist.png differ
diff --git a/addons/mail/static/src/img/feeds-hover.png b/addons/mail/static/src/img/feeds-hover.png
deleted file mode 100644
index 67070d3e7df..00000000000
Binary files a/addons/mail/static/src/img/feeds-hover.png and /dev/null differ
diff --git a/addons/mail/static/src/img/feeds.png b/addons/mail/static/src/img/feeds.png
deleted file mode 100644
index ac2293c0bf5..00000000000
Binary files a/addons/mail/static/src/img/feeds.png and /dev/null differ
diff --git a/addons/mail/static/src/img/formatting.png b/addons/mail/static/src/img/formatting.png
new file mode 100644
index 00000000000..cf45fdf192f
Binary files /dev/null and b/addons/mail/static/src/img/formatting.png differ
diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js
index 2140bbe474a..8ce050fd144 100644
--- a/addons/mail/static/src/js/mail.js
+++ b/addons/mail/static/src/js/mail.js
@@ -5,210 +5,652 @@ openerp.mail = function(session) {
var mail = session.mail = {};
/**
- * Add records to sorted_comments array
- * @param {Array} records records from mail.message sorted by date desc
- * @returns {Object} cs comments_structure: dict
- * cs.model_to_root_ids = {model: [root_ids], }
- * cs.new_root_ids = [new_root_ids]
- * cs.root_ids = [root_ids]
- * cs.msgs = {record.id: record,}
- * cs.tree_struct = {record.id: {
- * 'level': record_level in hierarchy, 0 is root,
- * 'msg_nbr': number of childs,
- * 'direct_childs': [msg_ids],
- * 'all_childs': [msg_ids],
- * 'for_thread_msgs': [records],
- * 'ancestors': [msg_ids], } }
+ * ------------------------------------------------------------
+ * FormView
+ * ------------------------------------------------------------
+ *
+ * Override of formview do_action method, to catch all return action about
+ * mail.compose.message. The purpose is to bind 'Send by e-mail' buttons
+ * and redirect them to the Chatter.
*/
- function tools_sort_comments(cs, records, parent_id) {
- var cur_iter = 0; var max_iter = 10; var modif = true;
- while ( modif && (cur_iter++) < max_iter) {
- modif = false;
- _(records).each(function (record) {
- // root and not yet recorded
- if ( (record.parent_id == false || record.parent_id[0] == parent_id) && ! cs['msgs'][record.id]) {
- // add to model -> root_list ids
- if (! cs['model_to_root_ids'][record.model]) cs['model_to_root_ids'][record.model] = [record.id];
- else cs['model_to_root_ids'][record.model].push(record.id);
- // add root data
- cs['new_root_ids'].push(record.id);
- // add record
- cs['tree_struct'][record.id] = {'level': 0, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [record], 'msg_nbr': -1, 'ancestors': []};
- cs['msgs'][record.id] = record;
- modif = true;
- }
- // not yet recorded, but parent is recorded
- else if (! cs['msgs'][record.id] && cs['msgs'][record.parent_id[0]]) {
- var parent_level = cs['tree_struct'][record.parent_id[0]]['level'];
- // update parent structure
- cs['tree_struct'][record.parent_id[0]]['direct_childs'].push(record.id);
- cs['tree_struct'][record.parent_id[0]]['for_thread_msgs'].push(record);
- // update ancestors structure
- for (ancestor_id in cs['tree_struct'][record.parent_id[0]]['ancestors']) {
- cs['tree_struct'][ancestor_id]['all_childs'].push(record.id);
+
+ session.web.FormView = session.web.FormView.extend({
+ do_action: function(action, on_close) {
+ if (action.res_model == 'mail.compose.message' && this.fields && this.fields.message_ids) {
+ var record_thread = this.fields.message_ids;
+ var thread = record_thread.thread;
+ thread.instantiate_composition_form('comment', true, false, 0, action.context);
+ return false;
+ }
+ else {
+ return this._super(action, on_close);
+ }
+ },
+ });
+
+
+ /**
+ * ------------------------------------------------------------
+ * ChatterUtils
+ * ------------------------------------------------------------
+ *
+ * This class holds a few tools method that will be used by
+ * the various Chatter widgets.
+ */
+
+ mail.ChatterUtils = {
+
+ /**
+ * mail_int_mapping: structure to keep a trace of internal links mapping
+ * mail_int_mapping['model'] = {
+ * 'name_get': [[id,label], [id,label], ...]
+ * 'fetch_ids': [id, id, ...] } */
+ //var mail_int_mapping = {};
+
+ /**
+ * mail_msg_struct: structure to orrganize chatter messages
+ */
+ //var mail_msg_struct = {}; // TODO: USE IT OR NOT :)
+
+ /* generic chatter events binding */
+ bind_events: function(widget) {
+ // event: click on an internal link to a document: model, login
+ widget.$element.delegate('a.oe_mail_internal_link', 'click', function (event) {
+ event.preventDefault();
+ // lazy implementation: fetch data and try to redirect
+ if (! event.srcElement.dataset.resModel) return false;
+ else var res_model = event.srcElement.dataset.resModel;
+ var res_login = event.srcElement.dataset.resLogin;
+ if (! res_login) return false;
+ var ds = new session.web.DataSet(widget, res_model);
+ var defer = ds.call('search', [[['login', '=', res_login]]]).pipe(function (records) {
+ if (records[0]) {
+ widget.do_action({ type: 'ir.actions.act_window', res_model: res_model, res_id: parseInt(records[0]), views: [[false, 'form']]});
}
- // add record
- cs['tree_struct'][record.id] = {'level': parent_level+1, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [], 'msg_nbr': -1, 'ancestors': []};
- cs['msgs'][record.id] = record;
- modif = true;
- }
+ else return false;
+ });
});
- }
- return cs;
- }
+ },
+
+ /** get an image in /web/binary/image?... */
+ get_image: function(session_prefix, session_id, model, field, id) {
+ return session_prefix + '/web/binary/image?session_id=' + session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
+ },
+
+ /** checks if tue current user is the message author */
+ is_author: function (widget, message_user_id) {
+ return (widget.session && widget.session.uid != 0 && widget.session.uid == message_user_id);
+ },
+
+ /**
+ * Add records to comments_structure array
+ * @param {Array} records records from mail.message sorted by date desc
+ * @returns {Object} cs comments_structure: dict
+ * cs.model_to_root_ids = {model: [root_ids], }
+ * cs.new_root_ids = [new_root_ids]
+ * cs.root_ids = [root_ids]
+ * cs.msgs = {record.id: record,}
+ * cs.tree_struct = {record.id: {
+ * 'level': record_level in hierarchy, 0 is root,
+ * 'msg_nbr': number of childs,
+ * 'direct_childs': [msg_ids],
+ * 'all_childs': [msg_ids],
+ * 'for_thread_msgs': [records],
+ * 'ancestors': [msg_ids], } }
+ */
+ records_struct_add_records: function(cs, records, parent_id) {
+ var cur_iter = 0; var max_iter = 10; var modif = true;
+ while ( modif && (cur_iter++) < max_iter) {
+ modif = false;
+ _(records).each(function (record) {
+ // root and not yet recorded
+ if ( (record.parent_id == false || record.parent_id[0] == parent_id) && ! cs['msgs'][record.id]) {
+ // add to model -> root_list ids
+ if (! cs['model_to_root_ids'][record.model]) cs['model_to_root_ids'][record.model] = [record.id];
+ else cs['model_to_root_ids'][record.model].push(record.id);
+ // add root data
+ cs['new_root_ids'].push(record.id);
+ // add record
+ cs['tree_struct'][record.id] = {'level': 0, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [record], 'msg_nbr': -1, 'ancestors': []};
+ cs['msgs'][record.id] = record;
+ modif = true;
+ }
+ // not yet recorded, but parent is recorded
+ else if (! cs['msgs'][record.id] && cs['msgs'][record.parent_id[0]]) {
+ var parent_level = cs['tree_struct'][record.parent_id[0]]['level'];
+ // update parent structure
+ cs['tree_struct'][record.parent_id[0]]['direct_childs'].push(record.id);
+ cs['tree_struct'][record.parent_id[0]]['for_thread_msgs'].push(record);
+ // update ancestors structure
+ for (ancestor_id in cs['tree_struct'][record.parent_id[0]]['ancestors']) {
+ cs['tree_struct'][ancestor_id]['all_childs'].push(record.id);
+ }
+ // add record
+ cs['tree_struct'][record.id] = {'level': parent_level+1, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [], 'msg_nbr': -1, 'ancestors': []};
+ cs['msgs'][record.id] = record;
+ modif = true;
+ }
+ });
+ }
+ return cs;
+ },
+
+ /* copy cs.new_root_ids into cs.root_ids */
+ records_struct_update_after_display: function(cs) {
+ // update TODO
+ cs['root_ids'] = _.union(cs['root_ids'], cs['new_root_ids']);
+ cs['new_root_ids'] = [];
+ return cs;
+ },
+
+ /**
+ * CONTENT MANIPULATION
+ *
+ * Regular expressions
+ * - (^|\s)@((\w|@|\.)*): @login@log.log, supports inner '@' for
+ * logins that are emails
+ * 1. '(void)'
+ * 2. login@log.log
+ * - (^|\s)\[(\w+).(\w+),(\d)\|*((\w|[@ .,])*)\]: [ir.attachment,3|My Label],
+ * for internal links to model ir.attachment, id=3, and with
+ * optional label 'My Label'. Note that having a '|Label' is not
+ * mandatory, because the regex should still be correct.
+ * 1. '(void)'
+ * 2. 'ir'
+ * 3. 'attachment'
+ * 4. '3'
+ * 5. 'My Label'
+ */
+
+ /** Removes html tags, except b, em, br, ul, li */
+ do_text_remove_html_tags: function (string) {
+ var html = $('
').text(string.replace(/\s+/g, ' ')).html().replace(new RegExp('<(/)?(b|em|br|br /|ul|li|div)\\s*>', 'gi'), '<$1$2>');
+ return html;
+ },
+
+ /** Replaces line breaks by html line breaks (br) */
+ do_text_nl2br: function (str, is_xhtml) {
+ var break_tag = (is_xhtml || typeof is_xhtml === 'undefined') ? '
' : '
';
+ return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2');
+ },
+
+ /* Add a prefix before each new line of the original string */
+ do_text_quote: function (str, prefix) {
+ return str.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2' + prefix || '> ');
+ },
+
+ /**
+ * Replaces some expressions
+ * - @login - shorcut to link to a res.user, given its login
+ * - [ir.attachment,3|My Label] - shortcut to an internal
+ * document
+ * - :name - shortcut to an image
+ */
+ do_replace_expressions: function (string) {
+ var self = this;
+ var icon_list = ['al', 'pinky']
+ /* shortcut to user: @login */
+ var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g);
+ var regex_res = regex_login.exec(string);
+ while (regex_res != null) {
+ var login = regex_res[2];
+ string = string.replace(regex_res[0], regex_res[1] + '
@' + login + ' ');
+ regex_res = regex_login.exec(string);
+ }
+ /* shortcut for internal document */
+ var regex_login = new RegExp(/(^|\s)\[(\w+).(\w+),(\d)\|*((\w|[@ .,])*)\]/g);
+ var regex_res = regex_login.exec(string);
+ while (regex_res != null) {
+ var res_model = regex_res[2] + '.' + regex_res[3];
+ var res_id = regex_res[4];
+ if (! regex_res[5]) {
+ var label = res_model + ':' + res_id }
+ else {
+ var label = regex_res[5];
+ }
+ string = string.replace(regex_res[0], regex_res[1] + '
');
+ regex_res = regex_login.exec(string);
+ }
+ return string;
+ },
+
+ /**
+ * Checks a string to find an expression that will be replaced
+ * by an internal link and requiring a name_get to replace
+ * the expression.
+ * :param mapping: structure to keep a trace of internal links mapping
+ * mapping['model'] = {
+ * name_get': [[id,label], [id,label], ...]
+ * 'to_fetch_ids': [id, id, ...]
+ * }
+ * CURRENTLY NOT IMPLEMENTED */
+ do_check_for_name_get_mapping: function(string, mapping) {
+ /* shortcut to user: @login */
+ //var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g);
+ //var regex_res = regex_login.exec(string);
+ //while (regex_res != null) {
+ //var login = regex_res[2];
+ //if (! ('res.users' in this.map_hash)) { this.map_hash['res.users']['name'] = []; }
+ //this.map_hash['res.users']['login'].push(login);
+ //regex_res = regex_login.exec(string);
+ //}
+ /* document link with name_get: [res.model,name] */
+ /* internal link with id: [res.model,id], or [res.model,id|display_name] */
+ //var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g);
+ //regex_res = regex_intlink.exec(string);
+ //while (regex_res != null) {
+ //var res_model = regex_res[2] + '.' + regex_res[3];
+ //var res_name = regex_res[4];
+ //if (! (res_model in this.map_hash)) { this.map_hash[res_model]['name'] = []; }
+ //this.map_hash[res_model]['name'].push(res_name);
+ //regex_res = regex_intlink.exec(string);
+ //}
+ },
+
+ /**
+ * Updates the mapping; check for to_fetch_ids for each recorded
+ * model, and perform a name_get to update the mapping.
+ * CURRENTLY NOT IMPLEMENTED */
+ do_update_name_get_mapping: function(mapping) {
+ },
+ };
+
+
+ /**
+ * ------------------------------------------------------------
+ * ComposeMessage widget
+ * ------------------------------------------------------------
+ *
+ * This widget handles the display of a form to compose a new message.
+ * This form is an OpenERP form_view, build on a mail.compose.message
+ * wizard.
+ */
+
+ mail.ComposeMessage = session.web.Widget.extend({
+ template: 'mail.compose_message',
+
+ /**
+ * @param {Object} parent parent
+ * @param {Object} [params]
+ * @param {String} [params.res_model] res_model of document [REQUIRED]
+ * @param {Number} [params.res_id] res_id of record [REQUIRED]
+ * @param {Number} [params.email_mode] true/false, tells whether
+ * we are in email sending mode
+ * @param {Number} [params.formatting] true/false, tells whether
+ * we are in advance formatting mode
+ * @param {String} [params.model] mail.compose.message.mode (see
+ * composition wizard)
+ * @param {Number} [params.msg_id] id of a message in case we are in
+ * reply mode
+ */
+ init: function(parent, params) {
+ var self = this;
+ this._super(parent);
+ // options
+ this.params = params || {};
+ this.params.context = params.context || {};
+ this.params.email_mode = params.email_mode || false;
+ this.params.formatting = params.formatting || false;
+ this.params.mode = params.mode || 'comment';
+ this.params.form_xml_id = params.form_xml_id || 'email_compose_message_wizard_form_chatter';
+ this.params.form_view_id = false;
+ if (this.params.mode == 'reply') {
+ this.params.active_id = this.params.msg_id;
+ } else {
+ this.params.active_id = this.params.res_id;
+ }
+ this.email_mode = false;
+ this.formatting = false;
+ },
+
+ /**
+ * Reinitialize the widget field values to the default values. The
+ * purpose is to avoid to destroy and re-build a form view. Default
+ * values are therefore given as for an onchange. */
+ reinit: function() {
+ var self = this;
+ if (! this.form_view) return;
+ var call_defer = this.ds_compose.call('default_get', [['subject', 'body_text', 'body_html', 'dest_partner_ids'], this.ds_compose.get_context()]).then(
+ function (result) {
+ self.form_view.on_processed_onchange({'value': result}, []);
+ });
+ return call_defer;
+ },
+
+ /**
+ * Override-hack of do_action: clean the form */
+ do_action: function(action, on_close) {
+ // this.init_comments();
+ return this._super(action, on_close);
+ },
+
+ /**
+ * Widget start function
+ * - builds and initializes the form view */
+ start: function() {
+ var self = this;
+ this._super.apply(this, arguments);
+ // customize display: add avatar, clean previous content
+ var user_avatar = mail.ChatterUtils.get_image(this.session.prefix,
+ this.session.session_id, 'res.users', 'avatar', this.session.uid);
+ this.$element.find('img.oe_mail_icon').attr('src', user_avatar);
+ this.$element.find('div.oe_mail_msg_content').empty();
+ // create a context for the default_get of the compose form
+ var widget_context = {
+ 'active_model': this.params.res_model,
+ 'active_id': this.params.active_id,
+ 'mail.compose.message.mode': this.params.mode,
+ };
+ var context = _.extend({}, this.params.context, widget_context);
+ this.ds_compose = new session.web.DataSetSearch(this, 'mail.compose.message', context);
+ // find the id of the view to display in the chatter form
+ var data_ds = new session.web.DataSetSearch(this, 'ir.model.data');
+ var deferred_form_id =data_ds.call('get_object_reference', ['mail', this.params.form_xml_id]).then( function (result) {
+ if (result) {
+ self.params.form_view_id = result[1];
+ }
+ }).pipe(this.proxy('create_form_view'));
+ return deferred_form_id;
+ },
+
+ /**
+ * Create a FormView, then append it to the to widget DOM. */
+ create_form_view: function () {
+ var self = this;
+ // destroy previous form_view if any
+ if (this.form_view) { this.form_view.destroy(); }
+ // create the FormView
+ this.form_view = new session.web.FormView(this, this.ds_compose, this.params.form_view_id, {
+ action_buttons: false,
+ pager: false,
+ initial_mode: 'edit',
+ });
+ // add the form, bind events, activate the form
+ var msg_node = this.$element.find('div.oe_mail_msg_content');
+ return $.when(this.form_view.appendTo(msg_node)).pipe(function() {
+ self.bind_events();
+ self.form_view.do_show();
+ if (self.params.email_mode) { self.toggle_email_mode(); }
+ if (self.params.formatting) { self.toggle_formatting_mode(); }
+ });
+ },
+
+ destroy: function() {
+ this._super.apply(this, arguments);
+ },
+
+ /**
+ * Bind events in the widget. Each event is slightly described
+ * in the function. */
+ bind_events: function() {
+ var self = this;
+ this.$element.find('button.oe_form_button').click(function (event) {
+ event.preventDefault();
+ });
+ // event: click on 'Send an Email' link that toggles the form for
+ // sending an email (partner_ids)
+ this.$element.find('a.oe_mail_compose_message_email').click(function (event) {
+ event.preventDefault();
+ self.toggle_email_mode();
+ });
+ // event: click on 'Formatting' icon-link that toggles the advanced
+ // formatting options for writing a message (subject, body_html)
+ this.$element.find('a.oe_mail_compose_message_formatting').click(function (event) {
+ event.preventDefault();
+ self.toggle_formatting_mode();
+ });
+ // event: click on 'Attachment' icon-link that opens the dialog to
+ // add an attachment.
+ this.$element.find('a.oe_mail_compose_message_attachment').click(function (event) {
+ event.preventDefault();
+ // not yet implemented
+ self.set_body_value('attachment', 'attachment');
+ });
+ // event: click on 'Checklist' icon-link that toggles the options
+ // for adding checklist.
+ this.$element.find('a.oe_mail_compose_message_checklist').click(function (event) {
+ event.preventDefault();
+ // not yet implemented
+ self.set_body_value('checklist', 'checklist');
+ });
+ },
+
+ /**
+ * Toggle the formatting mode. */
+ toggle_formatting_mode: function() {
+ var self = this;
+ this.formatting = ! this.formatting;
+ // calls onchange
+ var call_defer = this.ds_compose.call('onchange_formatting', [[], this.formatting, this.params.res_model, this.params.res_id]).then(
+ function (result) {
+ self.form_view.on_processed_onchange(result, []);
+ });
+ // update context of datasetsearch
+ this.ds_compose.context.formatting = this.formatting;
+ // toggle display
+ this.$element.find('span.oe_mail_compose_message_subject').toggleClass('oe_mail_compose_message_invisible');
+ this.$element.find('div.oe_mail_compose_message_body_text').toggleClass('oe_mail_compose_message_invisible');
+ this.$element.find('div.oe_mail_compose_message_body_html').toggleClass('oe_mail_compose_message_invisible');
+ },
+
+ /**
+ * Toggle the email mode. */
+ toggle_email_mode: function() {
+ var self = this;
+ this.email_mode = ! this.email_mode;
+ // calls onchange
+ var call_defer = this.ds_compose.call('onchange_email_mode', [[], this.email_mode, this.params.res_model, this.params.res_id]).then(
+ function (result) {
+ self.form_view.on_processed_onchange(result, []);
+ });
+ // update context of datasetsearch
+ this.ds_compose.context.email_mode = this.email_mode;
+ // update 'Post' button -> 'Send'
+ // update 'Send an Email' link -> 'Post a comment'
+ if (this.email_mode) {
+ this.$element.find('button.oe_mail_compose_message_button_send').html('
Send ');
+ this.$element.find('a.oe_mail_compose_message_email').html('Comment');
+ } else {
+ this.$element.find('button.oe_mail_compose_message_button_send').html('
Post ');
+ this.$element.find('a.oe_mail_compose_message_email').html('Send an Email');
+ }
+ // toggle display
+ this.$element.find('div.oe_mail_compose_message_partner_ids').toggleClass('oe_mail_compose_message_invisible');
+ },
+
+ /**
+ * Update the values of the composition form; with possible different
+ * values for body_text and body_html. */
+ set_body_value: function(body_text, body_html) {
+ this.form_view.fields.body_text.set_value(body_text);
+ this.form_view.fields.body_html.set_value(body_html);
+ },
+ }),
/**
- * ThreadDisplay widget: this widget handles the display of a thread of
- * messages. The [thread_level] parameter sets the thread level number:
+ * ------------------------------------------------------------
+ * Thread Widget
+ * ------------------------------------------------------------
+ *
+ * This widget handles the display of a thread of messages. The
+ * [thread_level] parameter sets the thread level number:
* - root message
* - - sub message (parent_id = root message)
* - - - sub sub message (parent id = sub message)
* - - sub message (parent_id = root message)
- * This widget has 2 ways of initialization, either you give records to be rendered,
- * either it will fetch [limit] messages related to [res_model]:[res_id].
+ * This widget has 2 ways of initialization, either you give records
+ * to be rendered, either it will fetch [limit] messages related to
+ * [res_model]:[res_id].
*/
+
mail.Thread = session.web.Widget.extend({
- template: 'mail.Thread',
+ template: 'mail.thread',
/**
* @param {Object} parent parent
* @param {Object} [params]
- * @param {String} [params.res_model] res_model of mail.thread object
- * @param {Number} [params.res_id] res_id of record
- * @param {Number} [params.parent_id=false] parent_id of message
- * @param {Number} [params.uid] user id
- * @param {Number} [params.thread_level=0] number of levels in the thread (only 0 or 1 currently)
- * @param {Number} [params.msg_more_limit=100] 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 (ex: 110 characters needed to be truncated and be displayed
- * as a 100-characters message)
- * @param {Number} [params.limit=10] maximum number of messages to fetch
+ * @param {String} [params.res_model] res_model of document [REQUIRED]
+ * @param {Number} [params.res_id] res_id of record [REQUIRED]
+ * @param {Number} [params.uid] user id [REQUIRED]
+ * @param {Bool} [params.parent_id=false] parent_id of message
+ * @param {Number} [params.thread_level=0] number of levels in the thread
+ * (only 0 or 1 currently)
+ * @param {Bool} [params.is_wall=false] thread is displayed in the wall
+ * @param {Number} [params.msg_more_limit=150] 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
+ * (ex: 110 characters needed to be truncated and be displayed as
+ * a 100-characters message)
+ * @param {Number} [params.limit=100] maximum number of messages to fetch
* @param {Number} [params.offset=0] offset for fetching messages
* @param {Number} [params.records=null] records to show instead of fetching messages
*/
init: function(parent, params) {
this._super(parent);
+ // options
this.params = params;
this.params.parent_id = this.params.parent_id || false;
this.params.thread_level = this.params.thread_level || 0;
- this.params.msg_more_limit = this.params.msg_more_limit || 290;
+ this.params.is_wall = this.params.is_wall || (this.params.records != undefined) || false;
+ this.params.msg_more_limit = this.params.msg_more_limit || 150;
this.params.limit = this.params.limit || 100;
+ // this.params.limit = 3; // tmp for testing
this.params.offset = this.params.offset || 0;
this.params.records = this.params.records || null;
// datasets and internal vars
- this.ds = new session.web.DataSet(this, this.params.res_model);
- this.ds_users = new session.web.DataSet(this, 'res.users');
- this.ds_msg = new session.web.DataSet(this, 'mail.message');
- this.sorted_comments = {'root_ids': [], 'root_id_msg_list': {}};
+ this.ds = new session.web.DataSetSearch(this, this.params.res_model);
+ this.ds_users = new session.web.DataSetSearch(this, 'res.users');
+ this.ds_msg = new session.web.DataSetSearch(this, 'mail.message');
this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}};
// display customization vars
this.display = {};
this.display.show_post_comment = this.params.show_post_comment || false;
- this.display.show_reply = (this.params.thread_level > 0);
- this.display.show_delete = true;
- this.display.show_hide = this.params.show_hide || false;
+ this.display.show_reply = (this.params.thread_level > 0 && this.params.is_wall);
+ this.display.show_delete = ! this.params.is_wall;
+ this.display.show_hide = this.params.is_wall;
+ this.display.show_reply_by_email = ! this.params.is_wall;
this.display.show_more = (this.params.thread_level == 0);
- // not used currently
- this.intlinks_mapping = {};
},
start: function() {
- var self = this;
-
this._super.apply(this, arguments);
- // customize display
- if (! this.display.show_post_comment) {
- this.$element.find('div.oe_mail_thread_action').hide();
- }
// add events
- this.add_events();
-
+ this.bind_events();
// display user, fetch comments
this.display_current_user();
-
- if (this.params.records) {
- var display_done = this.display_comments_from_parameters(this.params.records);
- } else {
- var display_done = this.init_comments();
+ if (this.params.records) var display_done = this.display_comments_from_parameters(this.params.records);
+ else var display_done = this.init_comments();
+ // customize display
+ $.when(display_done).then(this.proxy('do_customize_display'));
+ // add message composition form view
+ if (this.display.show_post_comment) {
+ var compose_done = this.instantiate_composition_form();
}
- return display_done
+ return display_done && compose_done;
},
-
- add_events: function() {
+
+ /**
+ * Override-hack of do_action: automatically reload the chatter.
+ * Normally it should be called only when clicking on 'Post/Send'
+ * in the composition form. */
+ do_action: function(action, on_close) {
+ this.init_comments();
+ if (this.compose_message_widget) {
+ this.compose_message_widget.reinit(); }
+ return this._super(action, on_close);
+ },
+
+ instantiate_composition_form: function(mode, email_mode, formatting, msg_id, context) {
+ if (this.compose_message_widget) {
+ this.compose_message_widget.destroy();
+ }
+ this.compose_message_widget = new mail.ComposeMessage(this, {
+ 'extended_mode': false, 'uid': this.params.uid, 'res_model': this.params.res_model,
+ 'res_id': this.params.res_id, 'mode': mode || 'comment', 'msg_id': msg_id,
+ 'email_mode': email_mode || false, 'formatting': formatting || false,
+ 'context': context || false } );
+ var composition_node = this.$element.find('div.oe_mail_thread_action');
+ composition_node.empty();
+ var compose_done = this.compose_message_widget.appendTo(composition_node);
+ return compose_done;
+ },
+
+ do_customize_display: function() {
+ if (this.display.show_post_comment) { this.$element.find('div.oe_mail_thread_action').eq(0).show(); }
+ },
+
+ /**
+ * Bind events in the widget. Each event is slightly described
+ * in the function. */
+ bind_events: function() {
var self = this;
+ // generic events from Chatter Mixin
+ mail.ChatterUtils.bind_events(this);
// event: click on 'more' at bottom of thread
this.$element.find('button.oe_mail_button_more').click(function () {
self.do_more();
});
- // event: writing in textarea
- this.$element.find('textarea.oe_mail_action_textarea').keyup(function (event) {
+ // event: writing in basic textarea of composition form (quick reply)
+ this.$element.find('textarea.oe_mail_compose_textarea').keyup(function (event) {
var charCode = (event.which) ? event.which : window.event.keyCode;
if (event.shiftKey && charCode == 13) { this.value = this.value+"\n"; }
else if (charCode == 13) { return self.do_comment(); }
});
- // event: click on 'reply' in msg
+ // event: click on 'Reply' in msg
this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_reply', 'click', function (event) {
var act_dom = $(this).parents('div.oe_mail_thread_display').find('div.oe_mail_thread_action:first');
act_dom.toggle();
event.preventDefault();
});
- // event: click on 'delete' in msg
- this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_delete', 'click', function (event) {
- //console.log('deleting');
- if (! confirm(_t("Do you really want to delete this message?"))) { return false; }
- var msg_id = event.srcElement.dataset.id;
- if (! msg_id) return false;
- var call_defer = self.ds_msg.unlink([parseInt(msg_id)]);
- $(event.srcElement).parents('.oe_mail_thread_msg').eq(0).hide();
- if (self.params.thread_level > 0) {
- $(event.srcElement).parents('.oe_mail_thread').eq(0).hide();
- }
- return false;
- });
- // event: click on 'hide' in msg
- this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_hide', 'click', function (event) {
- //console.log('hiding');
- if (! confirm(_t("Do you really want to hide this thread ?"))) { return false; }
- var msg_id = event.srcElement.dataset.id;
- if (! msg_id) return false;
- //console.log(msg_id);
- var call_defer = self.ds.call('message_remove_pushed_notifications', [[self.params.res_id], [parseInt(msg_id)], true]);
- $(event.srcElement).parents('.oe_mail_thread_msg').eq(0).hide();
- if (self.params.thread_level > 0) {
- $(event.srcElement).parents('.oe_mail_thread').eq(0).hide();
- }
- return false;
- });
- // event: click on an internal link
- this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_internal_link', 'click', function (event) {
- // lazy implementation: fetch data and try to redirect
- if (! event.srcElement.dataset.resModel) return false;
- else var res_model = event.srcElement.dataset.resModel;
- var res_login = event.srcElement.dataset.resLogin;
- var res_id = event.srcElement.dataset.resId;
- if ((! res_login) && (! res_id)) return false;
- if (! res_id) {
- var ds = new session.web.DataSet(self, res_model);
- var defer = ds.call('search', [[['login', '=', res_login]]]).then(function (records) {
- if (records[0]) {
- self.do_action({ type: 'ir.actions.act_window', res_model: res_model, res_id: parseInt(records[0]), views: [[false, 'form']]});
- }
- else return false;
- });
- }
- else self.do_action({ type: 'ir.actions.act_window', res_model: res_model, res_id: parseInt(res_id), views: [[false, 'form']]});
- });
// event: click on 'attachment(s)' in msg
this.$element.delegate('a.oe_mail_msg_view_attachments', 'click', function (event) {
var act_dom = $(this).parent().parent().parent().find('.oe_mail_msg_attachments');
act_dom.toggle();
- return false;
+ event.preventDefault();
});
- // see more
- this.$element.on('click','a.oe_mail_msg_more', function (event) {
- $(this).siblings('.oe_mail_msg_tail').show();
- $(this).hide();
- return false;
+ // event: click on 'Delete' in msg side menu
+ this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_delete', 'click', function (event) {
+ if (! confirm(_t("Do you really want to delete this message?"))) { return false; }
+ var msg_id = event.srcElement.dataset.id;
+ if (! msg_id) return false;
+ var call_defer = self.ds_msg.unlink([parseInt(msg_id)]);
+ $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).hide();
+ if (self.params.thread_level > 0) {
+ $(event.srcElement).parents('.oe_mail_thread').eq(0).hide();
+ }
+ event.preventDefault();
+ return call_defer;
+ });
+ // event: click on 'Hide' in msg side menu
+ this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_hide', 'click', function (event) {
+ if (! confirm(_t("Do you really want to hide this thread ?"))) { return false; }
+ var msg_id = event.srcElement.dataset.id;
+ if (! msg_id) return false;
+ var call_defer = self.ds.call('message_remove_pushed_notifications', [[self.params.res_id], [parseInt(msg_id)], true]);
+ $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).hide();
+ if (self.params.thread_level > 0) {
+ $(event.srcElement).parents('.oe_mail_thread').eq(0).hide();
+ }
+ event.preventDefault();
+ return call_defer;
+ });
+ // event: click on "Reply" in msg side menu (email style)
+ this.$element.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_reply_by_email', 'click', function (event) {
+ var msg_id = event.srcElement.dataset.msg_id;
+ var email_mode = (event.srcElement.dataset.type == 'email');
+ var formatting = (event.srcElement.dataset.formatting == 'html');
+ if (! msg_id) return false;
+ self.instantiate_composition_form('reply', email_mode, formatting, msg_id);
+ event.preventDefault();
});
},
@@ -219,21 +661,22 @@ openerp.mail = function(session) {
init_comments: function() {
var self = this;
this.params.offset = 0;
- this.sorted_comments = {'root_ids': [], 'root_id_msg_list': {}};
this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}};
this.$element.find('div.oe_mail_thread_display').empty();
- domain = this.get_fetch_domain(this.sorted_comments);
+ var domain = this.get_fetch_domain(this.comments_structure);
return this.fetch_comments(this.params.limit, this.params.offset, domain).then();
},
fetch_comments: function (limit, offset, domain) {
var self = this;
- var defer = this.ds.call('message_load', [[this.params.res_id], ( (limit+1)||(this.params.limit+1) ), (offset||this.params.offset), (domain||[]), (this.params.thread_level > 0), (this.sorted_comments['root_ids'])]);
- $.when(defer).then(function (records) {
+ var defer = this.ds.call('message_read', [[this.params.res_id], (this.params.thread_level > 0), (this.comments_structure['root_ids']),
+ (limit+1) || (this.params.limit+1), offset||this.params.offset, domain||undefined ]).then(function (records) {
if (records.length <= self.params.limit) self.display.show_more = false;
- else { self.display.show_more = true; records.pop(); }
-
+ // else { self.display.show_more = true; records.pop(); }
+ // else { self.display.show_more = true; records.splice(0, 1); }
+ else { self.display.show_more = true; }
self.display_comments(records);
+ // TODO: move to customize display
if (self.display.show_more == true) self.$element.find('div.oe_mail_thread_more:last').show();
else self.$element.find('div.oe_mail_thread_more:last').hide();
});
@@ -245,6 +688,7 @@ openerp.mail = function(session) {
if (records.length > 0 && records.length < (records[0].child_ids.length+1) ) this.display.show_more = true;
else this.display.show_more = false;
var defer = this.display_comments(records);
+ // TODO: move to customize display
if (this.display.show_more == true) $('div.oe_mail_thread_more').eq(-2).show();
else $('div.oe_mail_thread_more').eq(-2).hide();
return defer;
@@ -252,7 +696,8 @@ openerp.mail = function(session) {
display_comments: function (records) {
var self = this;
-
+ // sort the records
+ mail.ChatterUtils.records_struct_add_records(this.comments_structure, records, this.params.parent_id);
//build attachments download urls and compute time-relative from dates
for (var k in records) {
records[k].timerelative = $.timeago(records[k].date);
@@ -263,11 +708,10 @@ openerp.mail = function(session) {
}
}
}
- this.cs = this.sort_comments_tmp(records);
_(records).each(function (record) {
var sub_msgs = [];
if ((record.parent_id == false || record.parent_id[0] == self.params.parent_id) && self.params.thread_level > 0 ) {
- var sub_list = self.cs['tree_struct'][record.id]['direct_childs'];
+ var sub_list = self.comments_structure['tree_struct'][record.id]['direct_childs'];
_(records).each(function (record) {
//if (record.parent_id == false || record.parent_id[0] == self.params.parent_id) return;
if (_.indexOf(sub_list, record.id) != -1) {
@@ -276,35 +720,40 @@ openerp.mail = function(session) {
});
self.display_comment(record);
self.thread = new mail.Thread(self, {'res_model': self.params.res_model, 'res_id': self.params.res_id, 'uid': self.params.uid,
- 'records': sub_msgs, 'thread_level': (self.params.thread_level-1), 'parent_id': record.id});
- self.$element.find('.oe_mail_thread_msg:last').append('
');
+ 'records': sub_msgs, 'thread_level': (self.params.thread_level-1), 'parent_id': record.id,
+ 'is_wall': self.params.is_wall});
+ self.$element.find('li.oe_mail_thread_msg:last').append('
');
self.thread.appendTo(self.$element.find('div.oe_mail_thread_subthread:last'));
}
else if (self.params.thread_level == 0) {
self.display_comment(record);
}
});
+ mail.ChatterUtils.records_struct_update_after_display(this.comments_structure);
// update offset for "More" buttons
if (this.params.thread_level == 0) this.params.offset += records.length;
},
- /**
- * Display a record
- */
+ /** Displays a record, performs text/link formatting */
display_comment: function (record) {
- record.body = this.do_text_nl2br(record.body, true);
+ record.body = mail.ChatterUtils.do_text_nl2br(record.body, true);
+ // if (record.type == 'email' && record.state == 'received') {
if (record.type == 'email') {
record.mini_url = ('/mail/static/src/img/email_icon.png');
- } else {
- record.mini_url = this.thread_get_avatar('res.users', 'avatar', record.user_id[0]);
+ } else {
+ record.mini_url = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'avatar', record.user_id[0]);
}
// body text manipulation
- record.body = this.do_clean_text(record.body);
- record.body = this.do_replace_internal_links(record.body);
-
+ if (record.subtype == 'plain') {
+ record.body = mail.ChatterUtils.do_text_remove_html_tags(record.body);
+ }
+ record.body = mail.ChatterUtils.do_replace_expressions(record.body);
// format date according to the user timezone
record.date = session.web.format_value(record.date, {type:"datetime"});
- var rendered = session.web.qweb.render('mail.Thread.message', {'record': record, 'thread': this, 'params': this.params, 'display': this.display});
+ // is the user the author ?
+ record.is_author = mail.ChatterUtils.is_author(this, record.user_id[0]);
+ // render
+ var rendered = session.web.qweb.render('mail.thread.message', {'record': record, 'thread': this, 'params': this.params, 'display': this.display});
$(rendered).appendTo(this.$element.children('div.oe_mail_thread_display:first'));
// expand feature
this.$element.find('div.oe_mail_msg_body:last').expander({
@@ -316,84 +765,33 @@ openerp.mail = function(session) {
lesClass: 'oe_mail_reduce',
});
},
-
- /**
- * Add records to sorted_comments array
- * @param {Array} records records from mail.message sorted by date desc
- * @returns {Object} sc sorted_comments: dict {
- * 'root_id_list': list or root_ids
- * 'root_id_msg_list': {'record_id': [ancestor_ids]}, still sorted by date desc
- * 'id_to_root': {'root_id': [records]}, still sorted by date desc
- * }
- */
- sort_comments: function (records) {
- var self = this;
- sc = {'root_id_list': [], 'root_id_msg_list': {}, 'id_to_root': {}}
- var cur_iter = 0; var max_iter = 10; var modif = true;
- /* step1: get roots */
- while ( modif && (cur_iter++) < max_iter) {
- modif = false;
- _(records).each(function (record) {
- if ( (record.parent_id == false || record.parent_id[0] == self.params.parent_id) && (_.indexOf(sc['root_id_list'], record.id) == -1)) {
- sc['root_id_list'].push(record.id);
- sc['root_id_msg_list'][record.id] = [];
- self.sorted_comments['root_ids'].push(record.id);
- modif = true;
- }
- else {
- if (_.indexOf(sc['root_id_list'], record.parent_id[0]) != -1) {
- sc['id_to_root'][record.id] = record.parent_id[0];
- modif = true;
- }
- else if ( sc['id_to_root'][record.parent_id[0]] ) {
- sc['id_to_root'][record.id] = sc['id_to_root'][record.parent_id[0]];
- modif = true;
- }
- }
- });
- }
- /* step2: add records */
- _(records).each(function (record) {
- var root_id = sc['id_to_root'][record.id];
- if (! root_id) return;
- sc['root_id_msg_list'][root_id].push(record);
- //self.sorted_comments['root_id_msg_list'][root_id].push(record.id);
- });
- return sc;
- },
-
- /**
- * Add records to comments_structure object: see function for details
- */
- sort_comments_tmp: function(records) {
- return tools_sort_comments(this.comments_structure, records, this.params.parent_id);
- },
-
+
display_current_user: function () {
- return this.$element.find('img.oe_mail_msg_image').attr('src', this.thread_get_avatar('res.users', 'avatar', this.params.uid));
+ var avatar = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'avatar', this.params.uid);
+ return this.$element.find('img.oe_mail_icon').attr('src', avatar);
},
do_comment: function () {
var comment_node = this.$element.find('textarea');
var body_text = comment_node.val();
comment_node.val('');
- return this.ds.call('message_append_note', [[this.params.res_id], 'Reply', body_text, this.params.parent_id, 'comment', 'html']).then(
+ return this.ds.call('message_append_note', [[this.params.res_id], '', body_text, this.params.parent_id, 'comment', 'plain']).then(
this.proxy('init_comments'));
},
/**
* Create a domain to fetch new comments according to
- * comment already present in sorted_comments
- * @param {Object} sorted_comments (see sort_comments)
+ * comment already present in comments_structure
+ * @param {Object} comments_structure (see chatter utils)
* @returns {Array} fetch_domain (OpenERP domain style)
*/
- get_fetch_domain: function (sorted_comments) {
+ get_fetch_domain: function (comments_structure) {
var domain = [];
- var ids = sorted_comments.root_ids.slice();
+ var ids = comments_structure.root_ids.slice();
var ids2 = [];
// must be child of current parent
if (this.params.parent_id) { domain.push(['id', 'child_of', this.params.parent_id]); }
- _(sorted_comments.root_ids).each(function (id) { // each record
+ _(comments_structure.root_ids).each(function (id) { // each record
ids.push(id);
ids2.push(id);
});
@@ -413,109 +811,46 @@ openerp.mail = function(session) {
},
do_more: function () {
- domain = this.get_fetch_domain(this.sorted_comments);
+ domain = this.get_fetch_domain(this.comments_structure);
return this.fetch_comments(this.params.limit, this.params.offset, domain);
},
-
- /**
- *
- * var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g);
- * var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g);
- */
- do_replace_internal_links: function (string) {
- var self = this;
- var icon_list = ['al', 'pinky']
- /* shortcut to user: @login */
- var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g);
- var regex_res = regex_login.exec(string);
- while (regex_res != null) {
- var login = regex_res[2];
- string = string.replace(regex_res[0], regex_res[1] + '
@' + login + ' ');
- regex_res = regex_login.exec(string);
- }
- /* special shortcut: :name, try to find an icon if in list */
- var regex_login = new RegExp(/(^|\s):((\w)*)/g);
- var regex_res = regex_login.exec(string);
- while (regex_res != null) {
- var icon_name = regex_res[2];
- if (_.include(icon_list, icon_name))
- string = string.replace(regex_res[0], regex_res[1] + '
');
- regex_res = regex_login.exec(string);
- }
- return string;
- },
-
- thread_get_avatar: function(model, field, id) {
- return this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
- },
-
- /** Removes html tags, except b, em, br */
- do_clean_text: function (string) {
- var html = $('
').text(string.replace(/\s+/g, ' ')).html().replace(new RegExp('<(/)?(b|em|br|br /)\\s*>', 'gi'), '<$1$2>');
- return html;
- },
-
- /** Replaces line bracks by html line breaks (br) */
- do_text_nl2br: function (str, is_xhtml) {
- var break_tag = (is_xhtml || typeof is_xhtml === 'undefined') ? '
' : '
';
- return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2');
- },
-
- /**
- *
- * var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g);
- * var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g);
- */
- do_check_internal_links: function(string) {
- /* shortcut to user: @login */
- var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g);
- var regex_res = regex_login.exec(string);
- while (regex_res != null) {
- var login = regex_res[2];
- if (! ('res.users' in this.map_hash)) { this.map_hash['res.users']['name'] = []; }
- this.map_hash['res.users']['login'].push(login);
- regex_res = regex_login.exec(string);
- }
- /* internal links: #res.model,name */
- var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g);
- regex_res = regex_intlink.exec(string);
- while (regex_res != null) {
- var res_model = regex_res[2] + '.' + regex_res[3];
- var res_name = regex_res[4];
- if (! (res_model in this.map_hash)) { this.map_hash[res_model]['name'] = []; }
- this.map_hash[res_model]['name'].push(res_name);
- regex_res = regex_intlink.exec(string);
- }
- },
-
- /** checks if tue current user is the message author */
- _is_author: function (id) {
- return (this.session.uid == id);
- },
-
});
- session.web.form.widgets.add( 'Thread', 'openerp.mail.Thread');
+
+
+ /**
+ * ------------------------------------------------------------
+ * mail_thread Widget
+ * ------------------------------------------------------------
+ *
+ * This widget handles the display of the Chatter on documents.
+ */
+
+ /* Add mail_thread widget to registry */
+ session.web.form.widgets.add('mail_thread', 'openerp.mail.RecordThread');
/** mail_thread widget: thread of comments */
mail.RecordThread = session.web.form.AbstractField.extend({
- template: 'mail.RecordThread',
+ // QWeb template to use when rendering the object
+ template: 'mail.record_thread',
init: function() {
this._super.apply(this, arguments);
- this.see_subscribers = true;
- this.thread = null;
this.params = this.get_definition_options();
this.params.thread_level = this.params.thread_level || 0;
- // datasets
+ this.params.see_subscribers = true;
+ this.params.see_subscribers_options = this.params.see_subscribers_options || false;
+ this.thread = null;
this.ds = new session.web.DataSet(this, this.view.model);
this.ds_users = new session.web.DataSet(this, 'res.users');
},
start: function() {
- this._super.apply(this, arguments);
var self = this;
- // bind buttons
- this.$element.find('button.oe_mail_button_followers').click(function () { self.do_toggle_followers(); }).hide();
+ this._super.apply(this, arguments);
+ mail.ChatterUtils.bind_events(this);
+ this.$element.find('button.oe_mail_button_followers').click(function () { self.do_toggle_followers(); });
+ if (! this.params.see_subscribers_options) {
+ this.$element.find('button.oe_mail_button_followers').hide(); }
this.$element.find('button.oe_mail_button_follow').click(function () { self.do_follow(); })
.mouseover(function () { $(this).html('Follow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); })
.mouseleave(function () { $(this).html('Not following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); });
@@ -530,7 +865,8 @@ openerp.mail = function(session) {
},
reinit: function() {
- this.see_subscribers = true;
+ this.params.see_subscribers = true;
+ this.params.see_subscribers_options = this.params.see_subscribers_options || false;
this.$element.find('button.oe_mail_button_followers').html('Hide followers')
this.$element.find('button.oe_mail_button_follow').hide();
this.$element.find('button.oe_mail_button_unfollow').hide();
@@ -548,11 +884,11 @@ openerp.mail = function(session) {
// fetch followers
var fetch_sub_done = this.fetch_subscribers();
// create and render Thread widget
- this.$element.find('div.oe_mail_recthread_left').empty();
+ this.$element.find('div.oe_mail_recthread_main').empty();
if (this.thread) this.thread.destroy();
this.thread = new mail.Thread(this, {'res_model': this.view.model, 'res_id': this.view.datarecord.id, 'uid': this.session.uid,
- 'thread_level': this.params.thread_level, 'show_post_comment': true, 'limit': 15});
- var thread_done = this.thread.appendTo(this.$element.find('div.oe_mail_recthread_left'));
+ 'thread_level': this.params.thread_level, 'show_post_comment': true, 'limit': 15});
+ var thread_done = this.thread.appendTo(this.$element.find('div.oe_mail_recthread_main'));
return fetch_sub_done && thread_done;
},
@@ -567,9 +903,8 @@ openerp.mail = function(session) {
this.$element.find('div.oe_mail_recthread_followers h4').html('Followers (' + records.length + ')');
_(records).each(function (record) {
if (record.id == self.session.uid) { self.is_subscriber = true; }
- var mini_url = self.thread_get_avatar('res.users', 'avatar', record.id);
- $('
' +
- '' + record.name + ' ').appendTo(user_list);
+ record.avatar_url = mail.ChatterUtils.get_image(self.session.prefix, self.session.session_id, 'res.users', 'avatar', record.id);
+ $(session.web.qweb.render('mail.record_thread.subscriber', {'record': record})).appendTo(user_list);
});
if (self.is_subscriber) {
self.$element.find('button.oe_mail_button_follow').hide();
@@ -591,30 +926,35 @@ openerp.mail = function(session) {
},
do_toggle_followers: function () {
- this.see_subscribers = ! this.see_subscribers;
- if (this.see_subscribers) { this.$element.find('button.oe_mail_button_followers').html('Hide followers'); }
- else { this.$element.find('button.oe_mail_button_followers').html('Display followers'); }
+ this.params.see_subscribers = ! this.params.see_subscribers;
+ if (this.params.see_subscribers) { this.$element.find('button.oe_mail_button_followers').html('Hide followers'); }
+ else { this.$element.find('button.oe_mail_button_followers').html('Show followers'); }
this.$element.find('div.oe_mail_recthread_followers').toggle();
},
-
- thread_get_avatar: function(model, field, id) {
- return this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
- },
});
- session.web.form.widgets.add( 'mail_thread', 'openerp.mail.RecordThread');
- /** WallView widget: a wall of messages */
- mail.WallView = session.web.Widget.extend({
- template: 'mail.Wall',
+
+ /**
+ * ------------------------------------------------------------
+ * WallView Widget
+ * ------------------------------------------------------------
+ *
+ * This widget handles the display of the Chatter on the Wall.
+ */
+
+ /* Add WallView widget to registry */
+ session.web.client_actions.add('mail.wall', 'session.mail.Wall');
+
+ /* WallView widget: a wall of messages */
+ mail.Wall = session.web.Widget.extend({
+ template: 'mail.wall',
/**
* @param {Object} parent parent
* @param {Object} [params]
* @param {Number} [params.limit=20] number of messages to show and fetch
* @param {Number} [params.search_view_id=false] search view id for messages
- * @var {Array} sorted_comments records sorted by res_model and res_id
- * records.res_model = {res_ids}
- * records.res_model.res_id = [records]
+ * @var {Array} comments_structure (see chatter utils)
*/
init: function (parent, params) {
this._super(parent);
@@ -637,26 +977,49 @@ openerp.mail = function(session) {
start: function () {
this._super.apply(this, arguments);
- var self = this;
this.display_current_user();
// add events
this.add_event_handlers();
// load mail.message search view
var search_view_ready = this.load_search_view(this.params.search_view_id, {}, false);
+ // load composition form
+ var compose_done = this.instantiate_composition_form();
// fetch first threads
var comments_ready = this.init_and_fetch_comments(this.params.limit, 0);
- return (search_view_ready && comments_ready);
+ return (search_view_ready && comments_ready && compose_done);
},
-
- stop: function () {
+
+ /**
+ * Override-hack of do_action: automatically reload the chatter.
+ * Normally it should be called only when clicking on 'Post/Send'
+ * in the composition form. */
+ do_action: function(action, on_close) {
+ this.init_and_fetch_comments();
+ if (this.compose_message_widget) {
+ this.compose_message_widget.reinit(); }
+ return this._super(action, on_close);
+ },
+
+ destroy: function () {
this._super.apply(this, arguments);
},
-
+
+ instantiate_composition_form: function(mode, msg_id) {
+ if (this.compose_message_widget) {
+ this.compose_message_widget.destroy();
+ }
+ this.compose_message_widget = new mail.ComposeMessage(this, {
+ 'extended_mode': false, 'uid': this.session.uid, 'res_model': 'res.users',
+ 'res_id': this.session.uid, 'mode': mode || 'comment', 'msg_id': msg_id });
+ var composition_node = this.$element.find('div.oe_mail_wall_action');
+ composition_node.empty();
+ var compose_done = this.compose_message_widget.appendTo(composition_node);
+ return compose_done;
+ },
+
/** Add events */
add_event_handlers: function () {
var self = this;
- // post a comment
- this.$element.find('.oe_mail_wall_action button').click(function () { return self.do_comment(); });
// display more threads
this.$element.find('button.oe_mail_wall_button_more').click(function () { return self.do_more(); });
},
@@ -699,13 +1062,9 @@ openerp.mail = function(session) {
},
display_current_user: function () {
- return this.$element.find('img.oe_mail_msg_image').attr('src', this.thread_get_avatar('res.users', 'avatar', this.session.uid));
- },
-
- thread_get_avatar: function(model, field, id) {
- return this.session.prefix + '/web/binary/image?session_id=' + this.session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
- },
-
+ //return this.$element.find('img.oe_mail_msg_image').attr('src', this.thread_get_avatar('res.users', 'avatar', this.session.uid));
+ },
+
/**
* Initializes the wall and calls fetch_comments
* @param {Number} limit: number of notifications to fetch
@@ -735,8 +1094,8 @@ openerp.mail = function(session) {
else var fetch_domain = this.search['domain'];
if (additional_context) var fetch_context = _.extend(this.search['context'], additional_context);
else var fetch_context = this.search['context'];
- return this.ds_thread.call('get_pushed_messages',
- [[this.session.uid], (limit || 0), (offset || 0), fetch_domain, true, [], fetch_context]).then(this.proxy('display_comments'));
+ return this.ds_thread.call('message_get_pushed_messages',
+ [[this.session.uid], true, [], (limit || 0), (offset || 0), fetch_domain, fetch_context]).then(this.proxy('display_comments'));
},
/**
@@ -745,16 +1104,16 @@ openerp.mail = function(session) {
display_comments: function (records) {
var self = this;
this.do_update_show_more(records.length >= self.params.limit);
- this.sort_comments(records);
+ mail.ChatterUtils.records_struct_add_records(this.comments_structure, records, false);
_(this.comments_structure['new_root_ids']).each(function (root_id) {
var records = self.comments_structure.tree_struct[root_id]['for_thread_msgs'];
var model_name = self.comments_structure.msgs[root_id]['model'];
var res_id = self.comments_structure.msgs[root_id]['res_id'];
- var render_res = session.web.qweb.render('mail.WallThreadContainer', {});
+ var render_res = session.web.qweb.render('mail.wall_thread_container', {});
$('
').html(render_res).appendTo(self.$element.find('ul.oe_mail_wall_threads'));
var thread = new mail.Thread(self, {
'res_model': model_name, 'res_id': res_id, 'uid': self.session.uid, 'records': records,
- 'parent_id': false, 'thread_level': self.params.thread_level, 'show_hide': true}
+ 'parent_id': false, 'thread_level': self.params.thread_level, 'show_hide': true, 'is_wall': true}
);
self.thread_list.push(thread);
return thread.appendTo(self.$element.find('li.oe_mail_wall_thread:last'));
@@ -764,19 +1123,11 @@ openerp.mail = function(session) {
this.comments_structure['new_root_ids'] = [];
},
- /**
- * Add records to comments_structure object: see function for details
- */
- sort_comments: function(records) {
- tools_sort_comments(this.comments_structure, records, false);
- },
-
/**
* Create a domain to fetch new comments according to
- * comments already present in sorted_comments
+ * comments already present in comments_structure
* - for each model:
* -- should not be child of already displayed ids
- * @param {Object} sorted_comments (see sort_comments)
* @returns {Array} fetch_domain (OpenERP domain style)
*/
get_fetch_domain: function () {
@@ -801,17 +1152,5 @@ openerp.mail = function(session) {
var domain = this.get_fetch_domain();
return this.fetch_comments(this.params.limit, 0, domain);
},
-
- /** Action: Posts a comment */
- do_comment: function () {
- var comment_node = this.$element.find('.oe_mail_wall_action textarea');
- var body_text = comment_node.val();
- comment_node.val('');
- var call_done = this.ds_users.call('message_append_note', [[this.session.uid], 'Tweet', body_text, false, 'comment', 'html']).then(this.proxy('init_and_fetch_comments'));
- },
});
- session.web.client_actions.add('mail.all_feeds', 'session.mail.WallView');
-
};
-
-// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml
index 982fb8aeb2f..bf8d7fd1475 100644
--- a/addons/mail/static/src/xml/mail.xml
+++ b/addons/mail/static/src/xml/mail.xml
@@ -1,7 +1,11 @@
-
+
+
-
-
-
+
+
-
+
See more discussions
-
+
+
+
-
+
+
-
-
-
+
+
+
+
+
+
+
Not following
Following
- Display followers
+ Show followers
Followers
@@ -53,44 +71,87 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
Load more messages
-
+
-
+
-
+
-
+
+
diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py
index 1b19a383c10..d26511464f8 100644
--- a/addons/mail/wizard/mail_compose_message.py
+++ b/addons/mail/wizard/mail_compose_message.py
@@ -33,7 +33,7 @@ from ..mail_message import to_email
# main mako-like expression pattern
EXPRESSION_PATTERN = re.compile('(\$\{.+?\})')
-class mail_compose_message(osv.osv_memory):
+class mail_compose_message(osv.TransientModel):
"""Generic Email composition wizard. This wizard is meant to be inherited
at model and view level to provide specific wizard features.
@@ -41,13 +41,12 @@ class mail_compose_message(osv.osv_memory):
parameters, among which are:
* mail.compose.message.mode: if set to 'reply', the wizard is in
- reply mode and pre-populated with the original quote.
- If set to 'mass_mail', the wizard is in mass mailing
- where the mail details can contain template placeholders
- that will be merged with actual data before being sent
- to each recipient. Recipients will be derived from the
- records determined via ``context['active_model']`` and
- ``context['active_ids']``.
+ reply to a previous message mode and pre-populated with the original
+ quote. If set to 'comment', it means you are writing a new message to
+ be attached to a document. If set to 'mass_mail', the wizard is in
+ mass mailing where the mail details can contain template placeholders
+ that will be merged with actual data before being sent to each
+ recipient.
* active_model: model name of the document to which the mail being
composed is related
* active_id: id of the document to which the mail being composed is
@@ -61,119 +60,179 @@ class mail_compose_message(osv.osv_memory):
_description = 'Email composition wizard'
def default_get(self, cr, uid, fields, context=None):
- """Overridden to provide specific defaults depending on the context
- parameters.
+ """ Overridden to provide specific defaults depending on the context
+ parameters.
+
+ Composition mode
+ - comment: default mode; active_model, active_id = model and ID of a
+ document we are commenting,
+ - reply: active_id = ID of a mail.message to which we are replying.
+ From this message we can find the related model and res_id,
+ - mass_mailing mode: active_model, active_id = model and ID of a
+ document we are commenting,
:param dict context: several context values will modify the behavior
of the wizard, cfr. the class description.
"""
if context is None:
context = {}
+ compose_mode = context.get('mail.compose.message.mode', 'comment')
+ active_model = context.get('active_model')
+ active_id = context.get('active_id')
result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context)
+
+ # get default values according to the composition mode
vals = {}
- reply_mode = context.get('mail.compose.message.mode') == 'reply'
- if (not reply_mode) and context.get('active_model') and context.get('active_id'):
- # normal mode when sending an email related to any document, as specified by
- # active_model and active_id in context
- vals = self.get_value(cr, uid, context.get('active_model'), context.get('active_id'), context)
- elif reply_mode and context.get('active_id'):
- # reply mode, consider active_id is the ID of a mail.message to which we're
- # replying
- vals = self.get_message_data(cr, uid, int(context['active_id']), context)
- else:
- # default mode
- result['model'] = context.get('active_model', False)
+ if compose_mode in ['reply']:
+ vals = self.get_message_data(cr, uid, int(context['active_id']), context=context)
+ elif compose_mode in ['comment', 'mass_mail'] and active_model and active_id:
+ vals = self.get_value(cr, uid, active_model, active_id, context)
for field in vals:
if field in fields:
- result.update({field : vals[field]})
+ result[field] = vals[field]
# link to model and record if not done yet
- if not result.get('model') or not result.get('res_id'):
- active_model = context.get('active_model')
- res_id = context.get('active_id')
- if active_model and active_model not in (self._name, 'mail.message'):
- result['model'] = active_model
- if res_id:
- result['res_id'] = res_id
+ if not result.get('model') and active_model:
+ result['model'] = active_model
+ if not result.get('res_id') and active_id:
+ result['res_id'] = active_id
# Try to provide default email_from if not specified yet
if not result.get('email_from'):
- current_user = self.pool.get('res.users').browse(cr, uid, uid, context)
+ current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
result['email_from'] = current_user.user_email or False
return result
_columns = {
+ 'dest_partner_ids': fields.many2many('res.partner',
+ 'email_message_send_partner_rel',
+ 'wizard_id', 'partner_id', 'Destination partners',
+ help="When sending emails through the social network composition wizard"\
+ "you may choose to send a copy of the mail to partners."),
'attachment_ids': fields.many2many('ir.attachment','email_message_send_attachment_rel', 'wizard_id', 'attachment_id', 'Attachments'),
'auto_delete': fields.boolean('Auto Delete', help="Permanently delete emails after sending"),
'filter_id': fields.many2one('ir.filters', 'Filters'),
}
def get_value(self, cr, uid, model, res_id, context=None):
- """Returns a defaults-like dict with initial values for the composition
- wizard when sending an email related to the document record identified
- by ``model`` and ``res_id``.
+ """ Returns a defaults-like dict with initial values for the composition
+ wizard when sending an email related to the document record
+ identified by ``model`` and ``res_id``.
- The default implementation returns an empty dictionary, and is meant
- to be overridden by subclasses.
+ The default implementation returns an empty dictionary, and is meant
+ to be overridden by subclasses.
- :param str model: model name of the document record this mail is related to.
- :param int res_id: id of the document record this mail is related to.
- :param dict context: several context values will modify the behavior
- of the wizard, cfr. the class description.
+ :param str model: model name of the document record this mail is
+ related to.
+ :param int res_id: id of the document record this mail is related to.
+ :param dict context: several context values will modify the behavior
+ of the wizard, cfr. the class description.
"""
- return {}
+ result = {}
+ user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
+ result.update({
+ 'model': model,
+ 'res_id': res_id,
+ 'email_from': user.user_email or tools.config.get('email_from', False),
+ 'body_html': False,
+ 'body_text': False,
+ 'subject': False,
+ 'dest_partner_ids': [],
+ })
+ return result
+
+ def onchange_email_mode(self, cr, uid, ids, value, model, res_id, context=None):
+ """ email_mode (values: True or False). This onchange on the email mode
+ allows to have some specific behavior when going in email mode, or
+ when going out of email mode.
+ Basically, dest_partner_ids is reset when going out of email
+ mode.
+ This method can be overridden for models that want to have their
+ specific behavior.
+ Note that currently, this onchange is used in mail.js and called
+ manually on the form instantiated in the Chatter.
+ """
+ if not value:
+ return {'value': {'dest_partner_ids': []}}
+ return {'value': {}}
+
+ def onchange_formatting(self, cr, uid, ids, value, model, res_id, context=None):
+ """ onchange_formatting (values: True or False). This onchange on the
+ formatting allows to have some specific behavior when going in
+ formatting mode, or when going out of formatting.
+ Basically, subject is reset when going out of formatting mode.
+ This method can be overridden for models that want to have their
+ specific behavior.
+ Note that currently, this onchange is used in mail.js and called
+ manually on the form instantiated in the Chatter.
+ """
+ if not value:
+ return {'value': {'subject': False}}
+ return {'value': {}}
def get_message_data(self, cr, uid, message_id, context=None):
- """Returns a defaults-like dict with initial values for the composition
- wizard when replying to the given message (e.g. including the quote
- of the initial message, and the correct recipient).
- Should not be called unless ``context['mail.compose.message.mode'] == 'reply'``.
+ """ Returns a defaults-like dict with initial values for the composition
+ wizard when replying to the given message (e.g. including the quote
+ of the initial message, and the correct recipient). It should not be
+ called unless ``context['mail.compose.message.mode'] == 'reply'``.
- :param int message_id: id of the mail.message to which the user
- is replying.
- :param dict context: several context values will modify the behavior
- of the wizard, cfr. the class description.
- When calling this method, the ``'mail'`` value
- in the context should be ``'reply'``.
+ :param int message_id: id of the mail.message to which the user
+ is replying.
+ :param dict context: several context values will modify the behavior
+ of the wizard, cfr. the class description.
"""
if context is None:
context = {}
result = {}
- mail_message = self.pool.get('mail.message')
- if message_id:
- message_data = mail_message.browse(cr, uid, message_id, context)
- subject = tools.ustr(message_data.subject or '')
- # we use the plain text version of the original mail, by default,
- # as it is easier to quote than the HTML version.
- # XXX TODO: make it possible to switch to HTML on the fly
- current_user = self.pool.get('res.users').browse(cr, uid, uid, context)
- body = message_data.body_text or current_user.signature or ''
- if context.get('mail.compose.message.mode') == 'reply':
- sent_date = _('On %(date)s, ') % {'date': message_data.date} if message_data.date else ''
- sender = _('%(sender_name)s wrote:') % {'sender_name': tools.ustr(message_data.email_from or _('You'))}
- quoted_body = '> %s' % tools.ustr(body.replace('\n', "\n> ") or '')
- body = '\n'.join(["\n", (sent_date + sender), quoted_body])
- body += "\n" + (current_user.signature or '')
- re_prefix = _("Re:")
- if not (subject.startswith('Re:') or subject.startswith(re_prefix)):
- subject = "%s %s" % (re_prefix, subject)
- result.update({
- 'subtype' : 'plain', # default to the text version due to quoting
- 'body_text' : body,
- 'subject' : subject,
- 'attachment_ids' : [],
- 'model' : message_data.model or False,
- 'res_id' : message_data.res_id or False,
- 'email_from' : current_user.user_email or message_data.email_to or False,
- 'email_to' : message_data.reply_to or message_data.email_from or False,
- 'email_cc' : message_data.email_cc or False,
- 'user_id' : uid,
+ if not message_id:
+ return result
- # pass msg-id and references of mail we're replying to, to construct the
- # new ones later when sending
- 'message_id' : message_data.message_id or False,
- 'references' : message_data.references and tools.ustr(message_data.references) or False,
- })
+ current_user = self.pool.get('res.users').browse(cr, uid, uid, context)
+ message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context)
+ # Form the subject
+ re_prefix = _("Re:")
+ reply_subject = tools.ustr(message_data.subject or '')
+ if not (reply_subject.startswith('Re:') or reply_subject.startswith(re_prefix)):
+ reply_subject = "%s %s" % (re_prefix, reply_subject)
+ # Form the bodies (text and html). We use the plain text version of the
+ # original mail, by default, as it is easier to quote than the HTML
+ # version. TODO: make it possible to switch to HTML on the fly
+ sent_date = _('On %(date)s, ') % {'date': message_data.date} if message_data.date else ''
+ sender = _('%(sender_name)s wrote:') % {'sender_name': tools.ustr(message_data.email_from or _('You'))}
+ body_text = message_data.body_text or ''
+ body_html = message_data.body_html or ''
+ quoted_body_text = '> %s' % tools.ustr(body_text.replace('\n', "\n> ") or '')
+ quoted_body_html = '
%s ' % (tools.ustr(body_html)),
+ reply_body_text = '\n%s%s\n%s\n%s' % (sent_date, sender, quoted_body_text, current_user.signature)
+ reply_body_html = '
%s%s
%s
%s' % (sent_date, sender, quoted_body_html, current_user.signature)
+ # form dest_partner_ids
+ dest_partner_ids = [partner.id for partner in message_data.partner_ids]
+ # Update header and references
+ reply_headers = {}
+ reply_references = message_data.references and tools.ustr(message_data.references) or False
+ reply_message_id = message_data.message_id or False
+ if reply_message_id:
+ reply_references = (reply_references or '') + " " + message_data.message_id
+ reply_headers['In-Reply-To'] = message_data.message_id
+ # update the result
+ result.update({
+ 'body_text': reply_body_text,
+ 'body_html': reply_body_html,
+ 'subject': reply_subject,
+ 'attachment_ids': [],
+ 'dest_partner_ids': dest_partner_ids,
+ 'model': message_data.model or False,
+ 'res_id': message_data.res_id or False,
+ 'email_from': current_user.user_email or message_data.email_to or False,
+ 'email_to': message_data.reply_to or message_data.email_from or False,
+ 'email_cc': message_data.email_cc or False,
+ 'user_id': uid,
+ # pass msg-id and references of mail we're replying to, to construct the
+ # new ones later when sending
+ 'message_id': reply_message_id,
+ 'references': reply_references,
+ 'headers': reply_headers,
+ })
return result
def send_mail(self, cr, uid, ids, context=None):
@@ -190,76 +249,81 @@ class mail_compose_message(osv.osv_memory):
'''
if context is None:
context = {}
- mail_message = self.pool.get('mail.message')
- for mail in self.browse(cr, uid, ids, context=context):
+ # composition wizard options
+ email_mode = context.get('email_mode')
+ formatting = context.get('formatting')
+ mass_mail_mode = context.get('mail.compose.message.mode') == 'mass_mail'
+
+ mail_message_obj = self.pool.get('mail.message')
+ for mail_wiz in self.browse(cr, uid, ids, context=context):
+ # attachments
attachment = {}
- for attach in mail.attachment_ids:
+ for attach in mail_wiz.attachment_ids:
attachment[attach.datas_fname] = attach.datas and attach.datas.decode('base64')
+
+ # default values, according to the wizard options
+ subject = mail_wiz.subject if formatting else False
+ content_subtype = 'html' if formatting else 'plain'
+ type = 'email' if email_mode else 'comment'
+ state = 'outgoing' if email_mode else False
+ partner_ids = [partner.id for partner in mail_wiz.dest_partner_ids]
references = None
headers = {}
+ body = mail_wiz.body_html if content_subtype == 'html' else mail_wiz.body_text
- body = mail.body_html if mail.subtype == 'html' else mail.body_text
-
- # Get model, and check whether it is OpenChatter enabled, aka inherit from mail.thread
- if context.get('mail.compose.message.mode') == 'mass_mail':
- if context.get('active_ids') and context.get('active_model'):
- active_ids = context['active_ids']
- active_model = context['active_model']
- else:
- active_model = mail.model
- active_model_pool = self.pool.get(active_model)
- active_ids = active_model_pool.search(cr, uid, ast.literal_eval(mail.filter_id.domain), context=ast.literal_eval(mail.filter_id.context))
+ # get model, active_ids, and check if model is openchatter-enabled
+ if mass_mail_mode and context.get('active_ids') and context.get('active_model'):
+ active_ids = context['active_ids']
+ active_model = context['active_model']
+ elif mass_mail_mode:
+ active_model = mail_wiz.model
+ active_model_pool = self.pool.get(active_model)
+ active_ids = active_model_pool.search(cr, uid, ast.literal_eval(mail_wiz.filter_id.domain), context=ast.literal_eval(mail_wiz.filter_id.context))
else:
- active_model = mail.model
- active_ids = [int(mail.res_id)]
+ active_model = mail_wiz.model
+ active_ids = [mail_wiz.res_id]
active_model_pool = self.pool.get(active_model)
- if hasattr(active_model_pool, '_inherit') and 'mail.thread' in active_model_pool._inherit:
- mail_thread_enabled = True
- else:
- mail_thread_enabled = False
-
- # Reply Email
- if context.get('mail.compose.message.mode') == 'reply' and mail.message_id:
- references = (mail.references or '') + " " + mail.message_id
- headers['In-Reply-To'] = mail.message_id
+ mail_thread_enabled = hasattr(active_model_pool, 'message_append')
if context.get('mail.compose.message.mode') == 'mass_mail':
# Mass mailing: must render the template patterns
for active_id in active_ids:
- subject = self.render_template(cr, uid, mail.subject, active_model, active_id)
- rendered_body = self.render_template(cr, uid, body, active_model, active_id)
- email_from = self.render_template(cr, uid, mail.email_from, active_model, active_id)
- email_to = self.render_template(cr, uid, mail.email_to, active_model, active_id)
- email_cc = self.render_template(cr, uid, mail.email_cc, active_model, active_id)
- email_bcc = self.render_template(cr, uid, mail.email_bcc, active_model, active_id)
- reply_to = self.render_template(cr, uid, mail.reply_to, active_model, active_id)
-
+ rendered_subject = self.render_template(cr, uid, subject, active_model, active_id)
+ rendered_body_html = self.render_template(cr, uid, mail_wiz.body_html, active_model, active_id)
+ rendered_body_text = self.render_template(cr, uid, mail_wiz.body_text, active_model, active_id)
+ email_from = self.render_template(cr, uid, mail_wiz.email_from, active_model, active_id)
+ email_to = self.render_template(cr, uid, mail_wiz.email_to, active_model, active_id)
+ email_cc = self.render_template(cr, uid, mail_wiz.email_cc, active_model, active_id)
+ email_bcc = self.render_template(cr, uid, mail_wiz.email_bcc, active_model, active_id)
+ reply_to = self.render_template(cr, uid, mail_wiz.reply_to, active_model, active_id)
+
# in mass-mailing mode we only schedule the mail for sending, it will be
# processed as soon as the mail scheduler runs.
if mail_thread_enabled:
- active_model_pool.message_append(cr, uid, [active_id],
- subject, body_text=mail.body_text, body_html=mail.body_html, subtype=mail.subtype, state='outgoing',
- email_to=email_to, email_from=email_from, email_cc=email_cc, email_bcc=email_bcc,
+ active_model_pool.message_append(cr, uid, [active_id], rendered_subject, rendered_body_text, rendered_body_html,
+ type=type, content_subtype=content_subtype, state=state, partner_ids=partner_ids,
+ email_from=email_from, email_to=email_to, email_cc=email_cc, email_bcc=email_bcc,
reply_to=reply_to, references=references, attachments=attachment, headers=headers, context=context)
else:
- mail_message.schedule_with_attach(cr, uid, email_from, to_email(email_to), subject, rendered_body,
- model=mail.model, email_cc=to_email(email_cc), email_bcc=to_email(email_bcc), reply_to=reply_to,
- attachments=attachment, references=references, res_id=active_id,
- subtype=mail.subtype, headers=headers, context=context)
+ mail_message_obj.schedule_with_attach(cr, uid, email_from, to_email(email_to), subject, rendered_body_text,
+ model=mail_wiz.model, email_cc=to_email(email_cc), email_bcc=to_email(email_bcc), reply_to=reply_to,
+ attachments=attachment, references=references, res_id=active_id, partner_ids=partner_ids,
+ content_subtype=mail_wiz.content_subtype, headers=headers, context=context)
else:
# normal mode - no mass-mailing
if mail_thread_enabled:
- msg_ids = active_model_pool.message_append(cr, uid, active_ids,
- mail.subject, body_text=mail.body_text, body_html=mail.body_html, subtype=mail.subtype, state='outgoing',
- email_to=mail.email_to, email_from=mail.email_from, email_cc=mail.email_cc, email_bcc=mail.email_bcc,
- reply_to=mail.reply_to, references=references, attachments=attachment, headers=headers, context=context)
+ msg_ids = active_model_pool.message_append(cr, uid, active_ids, subject, mail_wiz.body_text, mail_wiz.body_html,
+ type=type, content_subtype=content_subtype, state=state, partner_ids=partner_ids,
+ email_from=mail_wiz.email_from, email_to=mail_wiz.email_to, email_cc=mail_wiz.email_cc, email_bcc=mail_wiz.email_bcc,
+ reply_to=mail_wiz.reply_to, references=references, attachments=attachment, headers=headers, context=context)
else:
- msg_ids = [mail_message.schedule_with_attach(cr, uid, mail.email_from, to_email(mail.email_to), mail.subject, body,
- model=mail.model, email_cc=to_email(mail.email_cc), email_bcc=to_email(mail.email_bcc), reply_to=mail.reply_to,
- attachments=attachment, references=references, res_id=int(mail.res_id),
- subtype=mail.subtype, headers=headers, context=context)]
+ msg_ids = [mail_message_obj.schedule_with_attach(cr, uid, mail_wiz.email_from, to_email(mail_wiz.email_to), subject, mail_wiz.body_text,
+ type=type, model=mail_wiz.model, email_cc=to_email(mail_wiz.email_cc), email_bcc=to_email(mail_wiz.email_bcc), reply_to=mail_wiz.reply_to,
+ attachments=attachment, references=references, res_id=int(mail_wiz.res_id), partner_ids=partner_ids,
+ content_subtype=mail_wiz.content_subtype, headers=headers, context=context)]
# in normal mode, we send the email immediately, as the user expects us to (delay should be sufficiently small)
- mail_message.send(cr, uid, msg_ids, context=context)
+ if type == 'email':
+ mail_message_obj.send(cr, uid, msg_ids, context=context)
return {'type': 'ir.actions.act_window_close'}
@@ -293,8 +357,7 @@ class mail_compose_message(osv.osv_memory):
return template and EXPRESSION_PATTERN.sub(merge, template)
-
-class mail_compose_message_extended(osv.osv_memory):
+class mail_compose_message_extended(osv.TransientModel):
""" Extension of 'mail.compose.message' to support default field values related
to CRM-like models that follow the following conventions:
@@ -309,26 +372,64 @@ class mail_compose_message_extended(osv.osv_memory):
_inherit = 'mail.compose.message'
def get_value(self, cr, uid, model, res_id, context=None):
- """Overrides the default implementation to provide more default field values
- related to the corresponding CRM case.
+ """ Overrides the default implementation to provide more default field values
+ related to the corresponding CRM case.
"""
- result = super(mail_compose_message_extended, self).get_value(cr, uid, model, res_id, context=context)
+ result = super(mail_compose_message_extended, self).get_value(cr, uid, model, res_id, context=context)
model_obj = self.pool.get(model)
if getattr(model_obj, '_mail_compose_message', False) and res_id:
data = model_obj.browse(cr, uid , res_id, context)
- user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
result.update({
- 'model': model,
- 'res_id': res_id,
- 'email_from': user.user_email or tools.config.get('email_from', False),
'email_to': data.email_from or False,
'email_cc': tools.ustr(data.email_cc or ''),
'subject': data.name or False,
- 'body_text': '\n' + tools.ustr(user.signature or ''),
- 'subtype': 'plain',
})
if hasattr(data, 'section_id'):
result['reply_to'] = data.section_id and data.section_id.reply_to or False
return result
+ def onchange_email_mode(self, cr, uid, ids, value, model, res_id, context=None):
+ """ Overrides the default implementation to provide default values for
+ dest_partner_ids. This method checks that a partner maching the
+ ``email_from`` of the record exists. It it does not exist, it
+ creates a new partner. The found or created partner is then added
+ in dest_partner_ids.
+ Partner check/creation valid inly if the value is True, and if
+ the model has the ``_mail_compose_message`` attribute.
+ """
+ result = super(mail_compose_message_extended, self).onchange_email_mode(cr, uid, ids, value, model, res_id, context=context)
+ model_obj = self.pool.get(model)
+ if not value or not (getattr(model_obj, '_mail_compose_message', False) and res_id):
+ return result
+ data = model_obj.browse(cr, uid , res_id, context=context)
+ partner_obj = self.pool.get('res.partner')
+ partner_ids = partner_obj.search(cr, uid, [('email', '=', data.email_from)], context=context)
+ if partner_ids:
+ partner_id = partner_ids[0]
+ else:
+ partner_id = partner_obj.name_create(cr, uid, data.email_from, context=context)[0]
+ result['value'].update({
+ 'dest_partner_ids': [partner_id],
+ 'email_cc': tools.ustr(data.email_cc or ''),
+ })
+ if hasattr(data, 'section_id'):
+ result['value']['reply_to'] = data.section_id and data.section_id.reply_to or False
+ return result
+
+ def onchange_formatting(self, cr, uid, ids, value, model, res_id, context=None):
+ """ Overrides the default implementation to provide default values for
+ the subject.
+ Subject re-creation valid only if the value is True, and if the
+ model has the ``_mail_compose_message`` attribute.
+ """
+ result = super(mail_compose_message_extended, self).onchange_formatting(cr, uid, ids, value, model, res_id, context=context)
+ model_obj = self.pool.get(model)
+ if not value or not (getattr(model_obj, '_mail_compose_message', False) and res_id):
+ return result
+ data = model_obj.browse(cr, uid , res_id, context=context)
+ result['value'].update({
+ 'subject': data.name or False,
+ })
+ return result
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml
index bcc11c4111d..8fa4eaa9c50 100644
--- a/addons/mail/wizard/mail_compose_message_view.xml
+++ b/addons/mail/wizard/mail_compose_message_view.xml
@@ -37,6 +37,50 @@
+
+ mail.compose.message.form
+ mail.compose.message
+ 18
+ form
+
+
+
+
+
Compose Email
mail.compose.message
diff --git a/addons/marketing/marketing_view.xml b/addons/marketing/marketing_view.xml
index a697d586cd4..b4c8a295241 100644
--- a/addons/marketing/marketing_view.xml
+++ b/addons/marketing/marketing_view.xml
@@ -2,14 +2,11 @@
-
+
+ sequence="120"/>
diff --git a/addons/marketing_campaign/res_partner_view.xml b/addons/marketing_campaign/res_partner_view.xml
index ebbefeeb1f4..ee619f041e8 100644
--- a/addons/marketing_campaign/res_partner_view.xml
+++ b/addons/marketing_campaign/res_partner_view.xml
@@ -2,27 +2,21 @@
-
-
- res.partner.crm.history.inherit1
- res.partner
- form
-
-
-
- False
-
-
-
+
res.partner.workitem.info.inherit
res.partner
form
-
-
-
+
+ False
+
+
+
+
+
+
diff --git a/addons/membership/membership.py b/addons/membership/membership.py
index dc4eadbada9..06b84ec80c5 100644
--- a/addons/membership/membership.py
+++ b/addons/membership/membership.py
@@ -315,11 +315,11 @@ class Partner(osv.osv):
return self._membership_state(*args, **kwargs)
_columns = {
- 'associate_member': fields.many2one('res.partner', 'Associate member',help="A member with whom you want to associate your membership.It will consider the membership state of the associated member."),
+ 'associate_member': fields.many2one('res.partner', 'Associate Member',help="A member with whom you want to associate your membership.It will consider the membership state of the associated member."),
'member_lines': fields.one2many('membership.membership_line', 'partner', 'Membership'),
- 'free_member': fields.boolean('Free member', help = "Select if you want to give membership free of cost."),
+ 'free_member': fields.boolean('Free Member', help = "Select if you want to give membership free of cost."),
'membership_amount': fields.float(
- 'Membership amount', digits=(16, 2),
+ 'Membership Amount', digits=(16, 2),
help = 'The price negotiated by the partner'),
'membership_state': fields.function(
__get_membership_state,
@@ -338,7 +338,7 @@ class Partner(osv.osv):
-Paid Member: A member who has paid the membership amount."""),
'membership_start': fields.function(
_membership_date, multi = 'membeship_start',
- string = 'Start membership date', type = 'date',
+ string = 'Start Membership Date', type = 'date',
store = {
'account.invoice': (_get_invoice_partner, ['state'], 10),
'membership.membership_line': (_get_partner_id, ['state'], 10, ),
@@ -346,7 +346,7 @@ class Partner(osv.osv):
}, help="Date from which membership becomes active."),
'membership_stop': fields.function(
_membership_date,
- string = 'Stop membership date', type='date', multi='membership_stop',
+ string = 'Stop Membership Date', type='date', multi='membership_stop',
store = {
'account.invoice': (_get_invoice_partner, ['state'], 10),
'membership.membership_line': (_get_partner_id, ['state'], 10),
@@ -354,7 +354,7 @@ class Partner(osv.osv):
}, help="Date until which membership remains active."),
'membership_cancel': fields.function(
_membership_date,
- string = 'Cancel membership date', type='date', multi='membership_cancel',
+ string = 'Cancel Membership Date', type='date', multi='membership_cancel',
store = {
'account.invoice': (_get_invoice_partner, ['state'], 11),
'membership.membership_line': (_get_partner_id, ['state'], 10),
diff --git a/addons/membership/membership_data.xml b/addons/membership/membership_data.xml
index 60c21fc6cf6..14fbb00f9a2 100644
--- a/addons/membership/membership_data.xml
+++ b/addons/membership/membership_data.xml
@@ -5,10 +5,10 @@
-
- Member Price
- member_price
-
+
+ Member Price
+ member_price
+
@@ -71,7 +69,7 @@
- -
+ -
@@ -282,7 +280,7 @@
-
+
product.normal.form
form
diff --git a/addons/mrp/__openerp__.py b/addons/mrp/__openerp__.py
index bdf6f63b755..d1f5625e09f 100644
--- a/addons/mrp/__openerp__.py
+++ b/addons/mrp/__openerp__.py
@@ -27,6 +27,7 @@
"website" : "http://www.openerp.com",
"category" : "Manufacturing",
"sequence": 18,
+ "summary": "Manufacturing, Production, Bill of Materials",
"images" : ["images/bill_of_materials.jpeg", "images/manufacturing_order.jpeg", "images/planning_manufacturing_order.jpeg", "images/production_analysis.jpeg", "images/production_dashboard.jpeg","images/routings.jpeg","images/work_centers.jpeg"],
"depends" : ["procurement", "stock", "resource", "purchase", "product","process"],
"description": """
diff --git a/addons/mrp/i18n/mn.po b/addons/mrp/i18n/mn.po
new file mode 100644
index 00000000000..7c97b68cc46
--- /dev/null
+++ b/addons/mrp/i18n/mn.po
@@ -0,0 +1,2231 @@
+# 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:49+0000\n"
+"PO-Revision-Date: 2012-07-21 12:33+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-07-22 04:45+0000\n"
+"X-Generator: Launchpad (build 15654)\n"
+
+#. module: mrp
+#: view:mrp.routing.workcenter:0
+msgid "Routing Work Centers"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,help:mrp.mrp_production_action
+msgid ""
+"Manufacturing Orders are usually proposed automatically by OpenERP based on "
+"the bill of materials and the procurement rules, but you can also create "
+"manufacturing orders manually. OpenERP will handle the consumption of the "
+"raw materials (stock decrease) and the production of the finished products "
+"(stock increase) when the order is processed."
+msgstr ""
+
+#. module: mrp
+#: help:mrp.production,location_src_id:0
+msgid "Location where the system will look for components."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,workcenter_lines:0
+msgid "Work Centers Utilisation"
+msgstr ""
+
+#. module: mrp
+#: model:product.template,name:mrp.product_sugar_product_template
+msgid "Sugar"
+msgstr ""
+
+#. module: mrp
+#: report:mrp.production.order:0
+msgid "No. Of Cycles"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.routing.workcenter,cycle_nbr:0
+msgid "Number of Cycles"
+msgstr ""
+
+#. module: mrp
+#: model:product.uom.categ,name:mrp.product_uom_categ_fluid
+msgid "Fluid"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_minimumstockprocure0
+msgid ""
+"The 'Minimum stock rule' allows the system to create procurement orders "
+"automatically as soon as the minimum stock is reached."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,picking_id:0
+msgid "Picking list"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/report/price.py:130
+#, python-format
+msgid "Hourly Cost"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/report/price.py:139
+#, python-format
+msgid "Cost Price per Uom"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Scrap Products"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.mrp_routing_action
+#: model:ir.ui.menu,name:mrp.menu_mrp_routing_action
+msgid "Routings"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter,product_id:0
+msgid "Work Center Product"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.bom:0
+msgid "Search Bill Of Material"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_stockproduct1
+msgid "For stockable products and consumables"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,name:mrp.process_transition_stockproduction0
+msgid "To Produce"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.routing.workcenter,cycle_nbr:0
+msgid ""
+"Number of iterations this work center has to do in the specified operation "
+"of the routing."
+msgstr ""
+
+#. module: mrp
+#: view:mrp.bom:0
+#: field:mrp.bom,code:0
+#: view:mrp.production:0
+#: field:mrp.production,name:0
+msgid "Reference"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Finished Products"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Manufacturing Orders which are currently in production."
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,name:mrp.process_transition_servicerfq0
+#: model:process.transition,name:mrp.process_transition_stockrfq0
+msgid "To Buy"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_purchaseprocure0
+msgid "The system launches automatically a RFQ to the preferred supplier."
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Products to Finish"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.bom,method:0
+msgid "Set / Pack"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+#: field:mrp.production,state:0
+msgid "State"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter,costs_hour:0
+msgid "Cost per hour"
+msgstr ""
+
+#. module: mrp
+#: model:product.template,name:mrp.product_orange_product_template
+msgid "Orange"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_servicemts0
+msgid ""
+"This is used in case of a service without any impact in the system, a "
+"training session for instance."
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "UOM"
+msgstr ""
+
+#. module: mrp
+#: field:change.production.qty,product_qty:0
+#: field:mrp.bom,product_qty:0
+#: field:mrp.production,product_qty:0
+#: field:mrp.production.product.line,product_qty:0
+msgid "Product Qty"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.workcenter,product_id:0
+msgid ""
+"Fill this product to track easily your production costs in the analytic "
+"accounting."
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_purchaseprocure0
+msgid "For purchased material"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom.revision,indice:0
+msgid "Revision"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,help:mrp.product_form_config_action
+msgid ""
+"Create a product form for everything you buy or sell. Specify a supplier if "
+"the product can be purchased."
+msgstr ""
+
+#. module: mrp
+#: model:ir.ui.menu,name:mrp.next_id_77
+msgid "Reporting"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter,costs_cycle_account_id:0
+msgid "Cycle Account"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/report/price.py:130
+#, python-format
+msgid "Work Cost"
+msgstr ""
+
+#. module: mrp
+#: report:bom.structure:0
+msgid "["
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,name:mrp.process_transition_procureserviceproduct0
+msgid "Procurement of services"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.workcenter:0
+msgid "Capacity Information"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,move_created_ids2:0
+msgid "Produced Products"
+msgstr ""
+
+#. module: mrp
+#: report:mrp.production.order:0
+msgid "Destination Location"
+msgstr ""
+
+#. module: mrp
+#: model:ir.ui.menu,name:mrp.menu_mrp_bom
+msgid "Master Data"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_stockmts0
+msgid ""
+"The system waits for the products to be available in the stock. These "
+"products are typically procured manually or through a minimum stock rule."
+msgstr ""
+
+#. module: mrp
+#: report:mrp.production.order:0
+msgid "Partner Ref"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,origin:0
+#: report:mrp.production.order:0
+msgid "Source Document"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,product_lines:0
+msgid "Scheduled goods"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.bom,type:0
+msgid "Sets / Phantom"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.bom,position:0
+msgid "Reference to a position in an external plan."
+msgstr ""
+
+#. module: mrp
+#: constraint:stock.move:0
+msgid "You try to assign a lot which is not from the same product"
+msgstr ""
+
+#. module: mrp
+#: model:product.template,name:mrp.product_cloth_product_template
+msgid "Cloth"
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_mrp_product_produce
+msgid "Product Produce"
+msgstr ""
+
+#. module: mrp
+#: constraint:mrp.bom:0
+msgid "Error ! You cannot create recursive BoM."
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_mrp_routing_workcenter
+msgid "Work Center Usage"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,name:mrp.process_transition_procurestockableproduct0
+msgid "Procurement of stockable Product"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.bom:0
+msgid "Default UOM"
+msgstr ""
+
+#. module: mrp
+#: sql_constraint:mrp.production:0
+#: sql_constraint:stock.picking:0
+msgid "Reference must be unique per Company!"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/report/price.py:139
+#: report:bom.structure:0
+#: field:mrp.product_price,number:0
+#: report:mrp.production.order:0
+#, python-format
+msgid "Quantity"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production.workcenter.line,hour:0
+msgid "Nbr of hours"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Confirm Production"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_stockproduct0
+msgid ""
+"The system creates an order (production or purchased) depending on the sold "
+"quantity and the products parameters."
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_stockproduction0
+msgid ""
+"In case the Supply method of the product is Produce, the system creates a "
+"production order."
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,help:mrp.mrp_property_action
+msgid ""
+"The Properties in OpenERP are used to select the right bill of materials for "
+"manufacturing a product when you have different ways of building the same "
+"product. You can assign several properties to each Bill of Materials. When a "
+"sales person creates a sales order, he can relate it to several properties "
+"and OpenERP will automatically select the BoM to use according the needs."
+msgstr ""
+
+#. module: mrp
+#: help:mrp.production,picking_id:0
+msgid ""
+"This is the Internal Picking List that brings the finished product to the "
+"production plan"
+msgstr ""
+
+#. module: mrp
+#: model:ir.ui.menu,name:mrp.menu_view_resource_calendar_search_mrp
+msgid "Working Time"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.action_report_in_out_picking_tree
+msgid "Weekly Stock Value Variation"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+#: field:mrp.production,date_planned_date:0
+#: report:mrp.production.order:0
+msgid "Scheduled Date"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.bom:0
+msgid "Component Product"
+msgstr ""
+
+#. module: mrp
+#: report:mrp.production.order:0
+msgid "Bill Of Material"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.routing,location_id:0
+msgid ""
+"Keep empty if you produce at the location where the finished products are "
+"needed.Set a location if you produce at a fixed location. This can be a "
+"partner location if you subcontract the manufacturing operations."
+msgstr ""
+
+#. module: mrp
+#: view:board.board:0
+msgid "Stock Value Variation"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.action2
+msgid "Bill of Materials Structure"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_serviceproduct0
+msgid "Product type is service"
+msgstr ""
+
+#. module: mrp
+#: sql_constraint:res.company:0
+msgid "The company name must be unique !"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,help:mrp.mrp_property_group_action
+msgid ""
+"Define specific property groups that can be assigned to the properties of "
+"your bill of materials."
+msgstr ""
+
+#. module: mrp
+#: help:mrp.workcenter,costs_cycle:0
+msgid "Specify Cost of Work Center per cycle."
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,name:mrp.process_transition_bom0
+msgid "Manufacturing decomposition"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_serviceproduct1
+msgid "For Services."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom.revision,date:0
+msgid "Modification Date"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.workcenter,costs_cycle_account_id:0
+#: help:mrp.workcenter,costs_hour_account_id:0
+msgid ""
+"Complete this only if you want automatic analytic accounting entries on "
+"production orders."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production.workcenter.line,cycle:0
+msgid "Nbr of cycles"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_orderrfq0
+#: model:process.node,note:mrp.process_node_rfq0
+msgid "Request for Quotation."
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_billofmaterialrouting0
+msgid ""
+"The Bill of Material is linked to a routing, i.e. the succession of work "
+"centers."
+msgstr ""
+
+#. module: mrp
+#: constraint:product.product:0
+msgid "Error: Invalid ean code"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,move_created_ids:0
+msgid "Products to Produce"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.routing:0
+#: field:mrp.routing,location_id:0
+msgid "Production Location"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Change Qty"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.action_configure_workcenter
+msgid "Configure your work centers"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Force Reservation"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom.revision,author_id:0
+msgid "Author"
+msgstr ""
+
+#. module: mrp
+#: field:report.mrp.inout,value:0
+msgid "Stock value"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.action_product_bom_structure
+msgid "Product BoM Structure"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Search Production"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/report/price.py:139
+#, python-format
+msgid "Supplier Price per Uom"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.routing.workcenter,sequence:0
+msgid ""
+"Gives the sequence order when displaying a list of routing Work Centers."
+msgstr ""
+
+#. module: mrp
+#: constraint:stock.move:0
+msgid "You can not move products from or to a location of the type view."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,child_complete_ids:0
+msgid "BoM Hierarchy"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,product_uom:0
+#: field:mrp.production,product_uom:0
+#: field:mrp.production.product.line,product_uom:0
+msgid "Product UOM"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.production,state:0
+msgid "Picking Exception"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,bom_lines:0
+msgid "BoM Lines"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter,time_start:0
+msgid "Time before prod."
+msgstr ""
+
+#. module: mrp
+#: help:mrp.routing,active:0
+msgid ""
+"If the active field is set to False, it will allow you to hide the routing "
+"without removing it."
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,name:mrp.process_transition_billofmaterialrouting0
+msgid "Material Routing"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+#: field:mrp.production,move_lines2:0
+#: report:mrp.production.order:0
+msgid "Consumed Products"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.action_mrp_workcenter_load_wizard
+#: model:ir.model,name:mrp.model_mrp_workcenter_load
+#: model:ir.model,name:mrp.model_report_workcenter_load
+msgid "Work Center Load"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/procurement.py:43
+#, python-format
+msgid "No BoM defined for this product !"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.mrp_bom_form_action2
+#: model:ir.ui.menu,name:mrp.menu_mrp_bom_form_action2
+msgid "Bill of Material Components"
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_stock_move
+msgid "Stock Move"
+msgstr ""
+
+#. module: mrp
+#: model:ir.ui.menu,name:mrp.menu_mrp_planning
+msgid "Planning"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Ready"
+msgstr ""
+
+#. module: mrp
+#: model:product.template,name:mrp.product_buttons_product_template
+msgid "Shirt Buttons"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.production,routing_id:0
+msgid ""
+"The list of operations (list of work centers) to produce the finished "
+"product. The routing is mainly used to compute work center costs during "
+"operations and to plan future loads on work centers based on production "
+"plannification."
+msgstr ""
+
+#. module: mrp
+#: help:mrp.workcenter,time_cycle:0
+msgid "Time in hours for doing one cycle."
+msgstr ""
+
+#. module: mrp
+#: constraint:mrp.bom:0
+msgid "BoM line product should not be same as BoM product."
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "In Production"
+msgstr ""
+
+#. module: mrp
+#: model:ir.ui.menu,name:mrp.menu_mrp_property
+msgid "Master Bill of Materials"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.bom,product_uos:0
+msgid ""
+"Product UOS (Unit of Sale) is the unit of measurement for the invoicing and "
+"promotion of stock."
+msgstr ""
+
+#. module: mrp
+#: view:mrp.product_price:0
+#: view:mrp.workcenter.load:0
+msgid "Print"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.bom:0
+#: view:mrp.workcenter:0
+msgid "Type"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_minimumstockrule0
+msgid "Linked to the 'Minimum stock rule' supplying method."
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.workcenter.load,time_unit:0
+msgid "Per month"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/wizard/change_production_qty.py:78
+#: code:addons/mrp/wizard/change_production_qty.py:83
+#, python-format
+msgid "Couldn't find bill of material for product"
+msgstr ""
+
+#. module: mrp
+#: report:bom.structure:0
+msgid "Product Name"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/mrp.py:503
+#, python-format
+msgid "Invalid action !"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.bom,product_efficiency:0
+msgid "A factor of 0.9 means a loss of 10% within the production process."
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/mrp.py:734
+#: code:addons/mrp/mrp.py:762
+#, python-format
+msgid "Warning!"
+msgstr ""
+
+#. module: mrp
+#: report:mrp.production.order:0
+msgid "Printing date"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,name:mrp.process_node_orderrfq0
+#: model:process.node,name:mrp.process_node_rfq0
+msgid "RFQ"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,name:mrp.process_transition_producttostockrules0
+msgid "Procurement rule"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Partial"
+msgstr ""
+
+#. module: mrp
+#: report:mrp.production.order:0
+msgid "WorkCenter"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_procureserviceproduct0
+msgid ""
+"Depending on the chosen method to 'supply' the service, the procurement "
+"order creates a RFQ for a subcontracting purchase order or waits until the "
+"service is done (= the delivery of the products)."
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.production,priority:0
+msgid "Urgent"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Manufacturing Orders which are waiting for raw materials."
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,help:mrp.mrp_workcenter_action
+msgid ""
+"Work Centers allow you to create and manage manufacturing units. They "
+"consist of workers and/or machines, which are considered as units for "
+"capacity and planning forecast. Keep in mind that the working time and "
+"resource leave are not taken into account in the time computation of the "
+"work center."
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_mrp_production
+msgid "Manufacturing Order"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,name:mrp.process_transition_productionprocureproducts0
+msgid "Procurement of raw material"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+#: field:mrp.production,cycle_total:0
+msgid "Total Cycles"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.production,state:0
+msgid "Ready to Produce"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom.revision,name:0
+msgid "Modification name"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.bom:0
+#: view:mrp.production:0
+msgid "Date"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,type:0
+msgid "BoM Type"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/procurement.py:45
+#, python-format
+msgid ""
+"Procurement '%s' has an exception: 'No BoM defined for this product !'"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.property:0
+msgid "Search"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/mrp.py:626
+#, python-format
+msgid "Could not cancel manufacturing order !"
+msgstr ""
+
+#. module: mrp
+#: field:report.workcenter.load,cycle:0
+msgid "Nbr of cycle"
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_res_company
+msgid "Companies"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/mrp.py:627
+#, python-format
+msgid ""
+"You must first cancel related internal picking attached to this "
+"manufacturing order."
+msgstr ""
+
+#. module: mrp
+#: model:process.node,name:mrp.process_node_minimumstockrule0
+#: model:process.node,name:mrp.process_node_productminimumstockrule0
+msgid "Minimum Stock"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/mrp.py:503
+#, python-format
+msgid "Cannot delete a manufacturing order in state '%s'"
+msgstr ""
+
+#. module: mrp
+#: model:ir.ui.menu,name:mrp.menus_dash_mrp
+msgid "Dashboard"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/report/price.py:160
+#: code:addons/mrp/report/price.py:211
+#, python-format
+msgid "Total Cost of %s %s"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,name:mrp.process_node_stockproduct0
+#: model:process.node,name:mrp.process_node_stockproduct1
+#: model:process.process,name:mrp.process_process_stockableproductprocess0
+msgid "Stockable Product"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/report/price.py:130
+#, python-format
+msgid "Work Center name"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.routing,code:0
+msgid "Code"
+msgstr ""
+
+#. module: mrp
+#: report:mrp.production.order:0
+msgid "No. Of Hours"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.property:0
+#: view:mrp.property.group:0
+msgid "Property Group"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Qty"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_production0
+msgid "Manufacturing Plan."
+msgstr ""
+
+#. module: mrp
+#: view:mrp.routing:0
+#: view:mrp.workcenter:0
+msgid "Inactive"
+msgstr ""
+
+#. module: mrp
+#: view:change.production.qty:0
+#: view:mrp.product.produce:0
+#: view:mrp.product_price:0
+#: view:mrp.production:0
+#: view:mrp.workcenter.load:0
+msgid "Cancel"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/wizard/change_production_qty.py:63
+#, python-format
+msgid "Active Id is not found"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_servicerfq0
+msgid ""
+"If the service has a 'Buy' supply method, this creates a RFQ, a "
+"subcontracting demand for instance."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,move_prod_id:0
+msgid "Move product"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Late"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,name:mrp.process_node_servicemts0
+msgid "Make to stock"
+msgstr ""
+
+#. module: mrp
+#: report:bom.structure:0
+msgid "BOM Name"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Start Production"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.open_board_manufacturing
+#: model:ir.ui.menu,name:mrp.menu_board_manufacturing
+msgid "Production Dashboard"
+msgstr ""
+
+#. module: mrp
+#: model:res.groups,name:mrp.group_mrp_manager
+msgid "Manager"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Source Loc."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,position:0
+msgid "Internal Reference"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_billofmaterial0
+msgid "Product's structure"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,name:0
+#: report:mrp.production.order:0
+#: field:mrp.production.product.line,name:0
+#: field:mrp.routing,name:0
+#: field:mrp.routing.workcenter,name:0
+msgid "Name"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.product.produce,mode:0
+msgid "Mode"
+msgstr ""
+
+#. module: mrp
+#: report:bom.structure:0
+msgid "]"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter.load,measure_unit:0
+msgid "Amount measuring unit"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,help:mrp.mrp_production_action_planning
+msgid ""
+"Manufacturing Orders describe the operations that need to be carried out and "
+"the raw materials usage for each production stage. You use specifications "
+"(bills of materials or BoM) to work out the raw material requirements and "
+"the manufacturing orders needed for the finished products. Once the bills of "
+"materials have been defined, OpenERP is capable of automatically deciding on "
+"the manufacturing route depending on the needs of the company."
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.todo.category,name:mrp.category_mrp_config
+msgid "MRP Management"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.workcenter,costs_hour:0
+msgid "Specify Cost of Work Center per hour."
+msgstr ""
+
+#. module: mrp
+#: help:mrp.workcenter,capacity_per_cycle:0
+msgid ""
+"Number of operations this Work Center can do in parallel. If this Work "
+"Center represents a team of 5 workers, the capacity per cycle is 5."
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.mrp_production_action3
+msgid "Manufacturing Orders in Progress"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.bom:0
+#: view:mrp.production:0
+#: view:mrp.property:0
+#: view:mrp.routing:0
+#: view:mrp.workcenter:0
+msgid "Group By..."
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/report/price.py:130
+#, python-format
+msgid "Cycles Cost"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.workcenter.load,measure_unit:0
+msgid "Amount in cycles"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,location_dest_id:0
+msgid "Finished Products Location"
+msgstr ""
+
+#. module: mrp
+#: model:ir.ui.menu,name:mrp.menu_pm_resources_config
+msgid "Resources"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.routing.workcenter,hour_nbr:0
+msgid ""
+"Time in hours for this Work Center to achieve the operation of the specified "
+"routing."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter,costs_journal_id:0
+msgid "Analytic Journal"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.mrp_workcenter_action
+#: model:ir.ui.menu,name:mrp.menu_view_resource_search_mrp
+#: field:mrp.routing,workcenter_lines:0
+msgid "Work Centers"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.workcenter.load,time_unit:0
+msgid "Per week"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,help:mrp.mrp_routing_action
+msgid ""
+"Routings allow you to create and manage the manufacturing operations that "
+"should be followed within your work centers in order to produce a product. "
+"They are attached to bills of materials that will define the required raw "
+"materials."
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.product_form_config_action
+msgid "Create or Import Products"
+msgstr ""
+
+#. module: mrp
+#: field:report.workcenter.load,hour:0
+msgid "Nbr of hour"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.routing:0
+msgid "Work Center Operations"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.routing:0
+msgid "Notes"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Manufacturing Orders which are ready to start production."
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_mrp_bom
+#: view:mrp.bom:0
+#: field:mrp.production,bom_id:0
+#: model:process.node,name:mrp.process_node_billofmaterial0
+msgid "Bill of Material"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.workcenter.load:0
+msgid "Select time unit"
+msgstr ""
+
+#. module: mrp
+#: view:report.workcenter.load:0
+msgid "Work Center load"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.production,location_dest_id:0
+msgid "Location where the system will stock the finished products."
+msgstr ""
+
+#. module: mrp
+#: help:mrp.routing.workcenter,routing_id:0
+msgid ""
+"Routing indicates all the Work Centers used, for how long and/or cycles.If "
+"Routing is indicated then,the third tab of a production order (Work Centers) "
+"will be automatically pre-completed."
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_producttostockrules0
+msgid ""
+"The Minimum Stock Rule is an automatic procurement rule based on a mini and "
+"maxi quantity. It's available in the Inventory management menu and "
+"configured by product."
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/report/price.py:187
+#, python-format
+msgid "Components Cost of %s %s"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.workcenter.load,time_unit:0
+msgid "Day by day"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.bom:0
+msgid "Revisions"
+msgstr ""
+
+#. module: mrp
+#: model:product.template,name:mrp.product_shirt_product_template
+msgid "Shirt"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,priority:0
+msgid "Priority"
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_stock_picking
+msgid "Picking List"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/mrp.py:1000
+#, python-format
+msgid "Manufacturing order '%s' is scheduled for the %s."
+msgstr ""
+
+#. module: mrp
+#: report:mrp.production.order:0
+msgid "Production Order N° :"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/mrp.py:647
+#, python-format
+msgid "Manufacturing order '%s' is ready to produce."
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_mrp_production_product_line
+msgid "Production Scheduled Product"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/report/price.py:204
+#, python-format
+msgid "Work Cost of %s %s"
+msgstr ""
+
+#. module: mrp
+#: help:res.company,manufacturing_lead:0
+msgid "Security days for each manufacturing operation."
+msgstr ""
+
+#. module: mrp
+#: model:product.template,name:mrp.product_water_product_template
+msgid "Water"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.bom:0
+msgid "Component Name"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,name:mrp.process_node_mts0
+#: model:process.transition,name:mrp.process_transition_servicemts0
+#: model:process.transition,name:mrp.process_transition_stockmts0
+msgid "Make to Stock"
+msgstr ""
+
+#. module: mrp
+#: constraint:mrp.production:0
+msgid "Order quantity cannot be negative or zero!"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,help:mrp.mrp_bom_form_action
+msgid ""
+"Master Bills of Materials allow you to create and manage the list of "
+"necessary raw materials used to make a finished product. OpenERP will use "
+"these BoMs to automatically propose manufacturing orders according to "
+"product needs. You can either create a bill of materials to define specific "
+"production steps or define a single multi-level bill of materials."
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_stockrfq0
+msgid ""
+"In case the Supply method of the product is Buy, the system creates a "
+"purchase order."
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_procurement_order
+msgid "Procurement"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.action_view_mrp_product_price_wizard
+#: view:mrp.product_price:0
+msgid "Product Cost Structure"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/report/price.py:139
+#, python-format
+msgid "Components suppliers"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Production Work Centers"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Split in production lots"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.workcenter:0
+msgid "Search for mrp workcenter"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.bom:0
+msgid "BoM Structure"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,date_start:0
+msgid "Start Date"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter,costs_hour_account_id:0
+msgid "Hour Account"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Destination Loc."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,method:0
+msgid "Method"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Pending"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/mrp.py:603
+#, python-format
+msgid "Couldn't find a bill of material for this product."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,active:0
+#: field:mrp.routing,active:0
+msgid "Active"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,name:mrp.process_node_procureproducts0
+msgid "Procure Products"
+msgstr ""
+
+#. module: mrp
+#: view:report.workcenter.load:0
+msgid "Work Center Loads"
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_mrp_bom_revision
+msgid "Bill of Material Revision"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.production,origin:0
+msgid ""
+"Reference of the document that generated this production order request."
+msgstr ""
+
+#. module: mrp
+#: sql_constraint:mrp.bom:0
+msgid ""
+"All product quantities must be greater than 0.\n"
+"You should install the mrp_subproduct module if you want to manage extra "
+"products on BoMs !"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Extra Information"
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_change_production_qty
+msgid "Change Quantity of Products"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_productionorder0
+msgid "Drives the procurement orders for raw material."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter,costs_general_account_id:0
+msgid "General Account"
+msgstr ""
+
+#. module: mrp
+#: report:mrp.production.order:0
+msgid "SO Number"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.production,state:0
+msgid "Done"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.mrp_production_action4
+msgid "Manufacturing Orders Waiting Products"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.production,priority:0
+msgid "Not urgent"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,user_id:0
+msgid "Responsible"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.mrp_production_action2
+msgid "Manufacturing Orders To Start"
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_mrp_workcenter
+#: field:mrp.production.workcenter.line,workcenter_id:0
+#: field:mrp.routing.workcenter,workcenter_id:0
+#: view:mrp.workcenter:0
+#: field:report.workcenter.load,workcenter_id:0
+msgid "Work Center"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter,capacity_per_cycle:0
+msgid "Capacity per Cycle"
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_product_product
+#: view:mrp.bom:0
+#: field:mrp.bom,product_id:0
+#: view:mrp.production:0
+#: field:mrp.production,product_id:0
+#: report:mrp.production.order:0
+#: field:mrp.production.product.line,product_id:0
+msgid "Product"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+#: field:mrp.production,hour_total:0
+msgid "Total Hours"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,location_src_id:0
+msgid "Raw Materials Location"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.product_price:0
+msgid "Print Cost Structure of Product."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,product_uos:0
+#: field:mrp.production.product.line,product_uos:0
+msgid "Product UOS"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Consume Products"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.act_mrp_product_produce
+#: view:mrp.product.produce:0
+#: view:mrp.production:0
+msgid "Produce"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,name:mrp.process_node_stock0
+#: model:process.transition,name:mrp.process_transition_servicemto0
+#: model:process.transition,name:mrp.process_transition_stockproduct0
+msgid "Make to Order"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/mrp.py:358
+#, python-format
+msgid "Copy"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.workcenter,note:0
+msgid ""
+"Description of the Work Center. Explain here what's a cycle according to "
+"this Work Center."
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production.lot.line:0
+msgid "Production Products"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,date_finished:0
+msgid "End Date"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter,resource_id:0
+msgid "Resource"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.bom,date_start:0
+#: help:mrp.bom,date_stop:0
+msgid "Validity of this BoM or component. Keep empty if it's always valid."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,product_uos:0
+msgid "Product UoS"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.production,priority:0
+msgid "Very Urgent"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.bom,routing_id:0
+msgid ""
+"The list of operations (list of work centers) to produce the finished "
+"product. The routing is mainly used to compute work center costs during "
+"operations and to plan future loads on work centers based on production "
+"planning."
+msgstr ""
+
+#. module: mrp
+#: view:change.production.qty:0
+msgid "Approve"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.property.group:0
+msgid "Properties categories"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.production.workcenter.line,sequence:0
+msgid "Gives the sequence order when displaying a list of work orders."
+msgstr ""
+
+#. module: mrp
+#: report:mrp.production.order:0
+msgid "Source Location"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+#: view:mrp.production.product.line:0
+msgid "Scheduled Products"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production.lot.line:0
+msgid "Production Products Consommation"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.act_product_manufacturing_open
+#: model:ir.actions.act_window,name:mrp.mrp_production_action
+#: model:ir.actions.act_window,name:mrp.mrp_production_action_planning
+#: model:ir.ui.menu,name:mrp.menu_mrp_production_action
+#: model:ir.ui.menu,name:mrp.menu_mrp_production_order_action
+#: view:mrp.production:0
+msgid "Manufacturing Orders"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.product.produce,mode:0
+msgid ""
+"'Consume only' mode will only consume the products with the quantity "
+"selected.\n"
+"'Consume & Produce' mode will consume as well as produce the products with "
+"the quantity selected and it will finish the production order when total "
+"ordered quantities are produced."
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+#: report:mrp.production.order:0
+msgid "Work Orders"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter,costs_cycle:0
+msgid "Cost per cycle"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,name:mrp.process_node_serviceproduct0
+#: model:process.node,name:mrp.process_node_serviceproduct1
+msgid "Service"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.production,state:0
+msgid "Cancelled"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.bom,product_uom:0
+msgid ""
+"UoM (Unit of Measure) is the unit of measurement for the inventory control"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/mrp.py:734
+#, python-format
+msgid ""
+"You are going to consume total %s quantities of \"%s\".\n"
+"But you can only consume up to total %s quantities."
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_bom0
+msgid ""
+"The Bill of Material is the product's decomposition. The components (that "
+"are products themselves) can also have their own Bill of Material (multi-"
+"level)."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,company_id:0
+#: field:mrp.production,company_id:0
+#: field:mrp.routing,company_id:0
+#: field:mrp.routing.workcenter,company_id:0
+#: view:mrp.workcenter:0
+msgid "Company"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.workcenter.load,measure_unit:0
+msgid "Amount in hours"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter,time_cycle:0
+msgid "Time for 1 cycle (hour)"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.report.xml,name:mrp.report_mrp_production_report
+#: field:mrp.production.product.line,production_id:0
+#: field:mrp.production.workcenter.line,production_id:0
+#: model:process.node,name:mrp.process_node_production0
+#: model:process.node,name:mrp.process_node_productionorder0
+msgid "Production Order"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_productminimumstockrule0
+msgid "Automatic procurement rule"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Compute Data"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,product_uos_qty:0
+msgid "Product UoS Qty"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/report/price.py:139
+#: view:mrp.bom:0
+#, python-format
+msgid "Components"
+msgstr ""
+
+#. module: mrp
+#: report:bom.structure:0
+#: model:ir.actions.report.xml,name:mrp.report_bom_structure
+msgid "BOM Structure"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,date_stop:0
+msgid "Valid Until"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,date_start:0
+msgid "Valid From"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.bom,type:0
+msgid "Normal BoM"
+msgstr ""
+
+#. module: mrp
+#: field:res.company,manufacturing_lead:0
+msgid "Manufacturing Lead Time"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,product_uos_qty:0
+#: field:mrp.production.product.line,product_uos_qty:0
+msgid "Product UOS Qty"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,help:mrp.action_report_in_out_picking_tree
+msgid ""
+"Weekly Stock Value Variation enables you to track the stock value evolution "
+"linked to manufacturing activities, receptions of products and delivery "
+"orders."
+msgstr ""
+
+#. module: mrp
+#: view:mrp.product.produce:0
+msgid "Confirm"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,product_efficiency:0
+msgid "Manufacturing Efficiency"
+msgstr ""
+
+#. module: mrp
+#: constraint:res.company:0
+msgid "Error! You can not create recursive companies."
+msgstr ""
+
+#. module: mrp
+#: help:mrp.bom,active:0
+msgid ""
+"If the active field is set to False, it will allow you to hide the bills of "
+"material without removing it."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,product_rounding:0
+msgid "Product Rounding"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.production,state:0
+msgid "New"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.product.produce,mode:0
+msgid "Consume Only"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Recreate Picking"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.bom,type:0
+msgid ""
+"If a sub-product is used in several products, it can be useful to create its "
+"own BoM. Though if you don't want separated production orders for this sub-"
+"product, select Set/Phantom as BoM type. If a Phantom BoM is used for a root "
+"product, it will be sold and shipped as a set of components, instead of "
+"being produced."
+msgstr ""
+
+#. module: mrp
+#: help:mrp.production,state:0
+msgid ""
+"When the production order is created the state is set to 'Draft'.\n"
+" If the order is confirmed the state is set to 'Waiting Goods'.\n"
+" If any exceptions are there, the state is set to 'Picking Exception'. "
+" \n"
+"If the stock is available then the state is set to 'Ready to Produce'.\n"
+" When the production gets started then the state is set to 'In Production'.\n"
+" When the production is over, the state is set to 'Done'."
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.bom,method:0
+msgid "On Order"
+msgstr ""
+
+#. module: mrp
+#: model:ir.ui.menu,name:mrp.menu_mrp_configuration
+#: view:res.company:0
+msgid "Configuration"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.bom:0
+msgid "Starting Date"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter,time_stop:0
+msgid "Time after prod."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.workcenter.load,time_unit:0
+msgid "Type of period"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Total Qty"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.routing.workcenter,hour_nbr:0
+msgid "Number of Hours"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.workcenter:0
+msgid "Costing Information"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,name:mrp.process_node_purchaseprocure0
+msgid "Procurement Orders"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.bom,product_rounding:0
+msgid "Rounding applied on the product quantity."
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_stock0
+msgid "Assignment from Production or Purchase Order."
+msgstr ""
+
+#. module: mrp
+#: field:mrp.routing.workcenter,routing_id:0
+msgid "Parent Routing"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.workcenter,time_start:0
+msgid "Time in hours for the setup."
+msgstr ""
+
+#. module: mrp
+#: model:product.template,name:mrp.product_orangejuice_product_template
+msgid "Orange Juice"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom.revision,bom_id:0
+#: field:procurement.order,bom_id:0
+msgid "BoM"
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_report_mrp_inout
+#: view:report.mrp.inout:0
+msgid "Stock value variation"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_mts0
+#: model:process.node,note:mrp.process_node_servicemts0
+msgid "Assignment from stock."
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.production,state:0
+msgid "Waiting Goods"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom.revision,last_indice:0
+msgid "last indice"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,revision_ids:0
+#: view:mrp.bom.revision:0
+msgid "BoM Revisions"
+msgstr ""
+
+#. module: mrp
+#: field:report.mrp.inout,date:0
+#: field:report.workcenter.load,name:0
+msgid "Week"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.production,priority:0
+msgid "Normal"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Production started late"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_routing0
+msgid "Manufacturing Steps."
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/report/price.py:146
+#: model:ir.actions.report.xml,name:mrp.report_cost_structure
+#, python-format
+msgid "Cost Structure"
+msgstr ""
+
+#. module: mrp
+#: model:res.groups,name:mrp.group_mrp_user
+msgid "User"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.product.produce,mode:0
+msgid "Consume & Produce"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,bom_id:0
+msgid "Parent BoM"
+msgstr ""
+
+#. module: mrp
+#: report:bom.structure:0
+msgid "BOM Ref"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,help:mrp.mrp_bom_form_action2
+msgid ""
+"Bills of materials components are components and sub-products used to create "
+"master bills of materials. Use this menu to search in which BoM a specific "
+"component is used."
+msgstr ""
+
+#. module: mrp
+#: model:product.uom,name:mrp.product_uom_litre
+msgid "Litre"
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/mrp.py:762
+#, python-format
+msgid ""
+"You are going to produce total %s quantities of \"%s\".\n"
+"But you can only produce up to total %s quantities."
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_stockproduct0
+msgid "Product type is Stockable or Consumable."
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/mrp.py:603
+#: code:addons/mrp/wizard/change_production_qty.py:78
+#: code:addons/mrp/wizard/change_production_qty.py:83
+#, python-format
+msgid "Error"
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.production,state:0
+msgid "Production Started"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.product.produce,product_qty:0
+msgid "Select Quantity"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.act_product_product_2_mrp_bom
+#: model:ir.actions.act_window,name:mrp.mrp_bom_form_action
+#: model:ir.ui.menu,name:mrp.menu_mrp_bom_form_action
+#: field:product.product,bom_ids:0
+msgid "Bill of Materials"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.mrp_property_action
+#: model:ir.ui.menu,name:mrp.menu_mrp_property_action
+#: view:mrp.bom:0
+#: field:mrp.bom,property_ids:0
+#: view:mrp.property:0
+#: view:procurement.order:0
+#: field:procurement.order,property_ids:0
+msgid "Properties"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.routing.workcenter:0
+#: view:mrp.workcenter:0
+msgid "General Information"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+msgid "Productions"
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_mrp_routing
+#: view:mrp.bom:0
+#: field:mrp.bom,routing_id:0
+#: view:mrp.production:0
+#: field:mrp.production,routing_id:0
+#: view:mrp.routing:0
+#: model:process.node,name:mrp.process_node_routing0
+msgid "Routing"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,date_planned:0
+msgid "Scheduled date"
+msgstr ""
+
+#. module: mrp
+#: constraint:stock.move:0
+msgid "You must assign a production lot for this product"
+msgstr ""
+
+#. module: mrp
+#: field:stock.move,production_id:0
+msgid "Production"
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_mrp_production_workcenter_line
+#: field:mrp.production.workcenter.line,name:0
+msgid "Work Order"
+msgstr ""
+
+#. module: mrp
+#: view:board.board:0
+msgid "Procurements in Exception"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,name:mrp.process_transition_minimumstockprocure0
+msgid "'Minimum stock rule' material"
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_mrp_product_price
+msgid "Product Price"
+msgstr ""
+
+#. module: mrp
+#: model:ir.model,name:mrp.model_stock_move_split
+msgid "Split in Production lots"
+msgstr ""
+
+#. module: mrp
+#: view:change.production.qty:0
+msgid "Change Quantity"
+msgstr ""
+
+#. module: mrp
+#: view:change.production.qty:0
+#: model:ir.actions.act_window,name:mrp.action_change_production_qty
+msgid "Change Product Qty"
+msgstr ""
+
+#. module: mrp
+#: view:mrp.bom.revision:0
+#: field:mrp.bom.revision,description:0
+#: view:mrp.property:0
+#: view:mrp.property.group:0
+#: field:mrp.routing,note:0
+#: view:mrp.routing.workcenter:0
+#: field:mrp.routing.workcenter,note:0
+#: view:mrp.workcenter:0
+#: field:mrp.workcenter,note:0
+msgid "Description"
+msgstr ""
+
+#. module: mrp
+#: view:board.board:0
+msgid "Manufacturing board"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.production,date_planned_end:0
+msgid "Scheduled End Date"
+msgstr ""
+
+#. module: mrp
+#: model:process.node,note:mrp.process_node_procureproducts0
+msgid "The way to procurement depends on the product type."
+msgstr ""
+
+#. module: mrp
+#: model:ir.ui.menu,name:mrp.menu_mrp_manufacturing
+msgid "Manufacturing"
+msgstr ""
+
+#. module: mrp
+#: view:board.board:0
+msgid "Next Production Orders"
+msgstr ""
+
+#. module: mrp
+#: model:ir.actions.act_window,name:mrp.mrp_property_group_action
+#: model:ir.ui.menu,name:mrp.menu_mrp_property_group_action
+msgid "Property Groups"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_procurestockableproduct0
+msgid ""
+"Depending on the chosen method to supply the stockable products, the "
+"procurement order creates a RFQ, a production order, ... "
+msgstr ""
+
+#. module: mrp
+#: code:addons/mrp/mrp.py:874
+#: code:addons/mrp/mrp.py:929
+#: code:addons/mrp/mrp.py:954
+#, python-format
+msgid "PROD: %s"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.workcenter,time_stop:0
+msgid "Time in hours for the cleaning."
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,name:mrp.process_transition_purchaseprocure0
+msgid "Automatic RFQ"
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_servicemto0
+msgid ""
+"If the service has a 'Produce' supply method, this creates a task in the "
+"project management module of OpenERP."
+msgstr ""
+
+#. module: mrp
+#: model:process.transition,note:mrp.process_transition_productionprocureproducts0
+msgid ""
+"In order to supply raw material (to be purchased or produced), the "
+"production order creates as much procurement orders as components listed in "
+"the BOM, through a run of the schedulers (MRP)."
+msgstr ""
+
+#. module: mrp
+#: help:mrp.product_price,number:0
+msgid ""
+"Specify quantity of products to produce or buy. Report of Cost structure "
+"will be displayed base on this quantity."
+msgstr ""
+
+#. module: mrp
+#: selection:mrp.bom,method:0
+msgid "On Stock"
+msgstr ""
+
+#. module: mrp
+#: field:mrp.bom,sequence:0
+#: report:mrp.production.order:0
+#: field:mrp.production.workcenter.line,sequence:0
+#: field:mrp.routing.workcenter,sequence:0
+msgid "Sequence"
+msgstr ""
+
+#. module: mrp
+#: model:ir.ui.menu,name:mrp.menu_view_resource_calendar_leaves_search_mrp
+msgid "Resource Leaves"
+msgstr ""
+
+#. module: mrp
+#: help:mrp.bom,sequence:0
+msgid "Gives the sequence order when displaying a list of bills of material."
+msgstr ""
+
+#. module: mrp
+#: view:mrp.production:0
+#: field:mrp.production,move_lines:0
+#: report:mrp.production.order:0
+msgid "Products to Consume"
+msgstr ""
diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml
index 932f4ac2928..2d3e90c341a 100644
--- a/addons/mrp/mrp_view.xml
+++ b/addons/mrp/mrp_view.xml
@@ -2,17 +2,21 @@
-
+
+
-
+
+ sequence="5"/>
+
+
+
+
+
+ Fullscreen
+ Return to Record
+
+
+
+
+
+
diff --git a/addons/pad_project/__init__.py b/addons/pad_project/__init__.py
index d6bf9f9551c..098de31a5a4 100644
--- a/addons/pad_project/__init__.py
+++ b/addons/pad_project/__init__.py
@@ -19,6 +19,6 @@
#
##############################################################################
-import models
+import project_task
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/pad_project/__openerp__.py b/addons/pad_project/__openerp__.py
index 8ca761d2737..18c7d3191d3 100644
--- a/addons/pad_project/__openerp__.py
+++ b/addons/pad_project/__openerp__.py
@@ -20,7 +20,7 @@
##############################################################################
{
- 'name': 'Specifications on PADs',
+ 'name': 'Pad on tasks',
'version': '1.0',
"category": "Project Management",
'description': """
@@ -31,9 +31,9 @@ This module adds a PAD in all project kanban views
'website': 'http://www.openerp.com',
'depends': ['project', 'pad'],
'init_xml': [],
- 'update_xml': ['models/project_task.xml'],
+ 'update_xml': ['project_task.xml'],
'demo_xml': [],
'installable': True,
- 'auto_install': False,
+ 'auto_install': True,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/pad_project/models/__init__.py b/addons/pad_project/models/__init__.py
deleted file mode 100644
index 098de31a5a4..00000000000
--- a/addons/pad_project/models/__init__.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-
-import project_task
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/pad_project/models/project_task.py b/addons/pad_project/models/project_task.py
deleted file mode 100644
index c215aed1325..00000000000
--- a/addons/pad_project/models/project_task.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# -*- coding: utf-8 -*-
-from tools.translate import _
-from osv import fields, osv
-
-class task(osv.osv):
- _inherit = "project.task"
-
- def pad_get(self, cr, uid, ids, context=None):
- """Get pad action
- """
- url = self.pool.get("ir.attachment").pad_get(cr, uid, self._name, ids[0])
- return {
- 'type': 'ir.actions.act_url',
- 'url': url
- }
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/pad_project/models/project_task.xml b/addons/pad_project/models/project_task.xml
deleted file mode 100644
index 3c734cf3b97..00000000000
--- a/addons/pad_project/models/project_task.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
- project.task.kanban.pad
- project.task
- kanban
-
-
-
- PAD
-
-
-
-
-
diff --git a/addons/pad_project/project_task.py b/addons/pad_project/project_task.py
new file mode 100644
index 00000000000..46c2c54cd46
--- /dev/null
+++ b/addons/pad_project/project_task.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+from tools.translate import _
+from osv import fields, osv
+
+class task(osv.osv):
+ _name = "project.task"
+ _inherit = ["project.task",'pad.common']
+ _pad_fields = ['description_pad']
+ _columns = {
+ 'description_pad': fields.char('Description PAD', size=250)
+ }
+ _defaults = {
+ 'description_pad': lambda self, cr, uid, context: self.pad_generate_url(cr, uid, context),
+ }
diff --git a/addons/pad_project/project_task.xml b/addons/pad_project/project_task.xml
new file mode 100644
index 00000000000..d18f43a92f4
--- /dev/null
+++ b/addons/pad_project/project_task.xml
@@ -0,0 +1,15 @@
+
+
+
+ project.task.form.pad
+ project.task
+ form
+
+
+
+
+
+
+
+
+
diff --git a/addons/plugin_outlook/plugin_outlook.xml b/addons/plugin_outlook/plugin_outlook.xml
index 811661bf127..04e5dca32de 100644
--- a/addons/plugin_outlook/plugin_outlook.xml
+++ b/addons/plugin_outlook/plugin_outlook.xml
@@ -11,9 +11,6 @@
-
-
-
@@ -29,7 +26,7 @@
- Install Outlook Plug-In
+
diff --git a/addons/plugin_thunderbird/plugin_thunderbird.xml b/addons/plugin_thunderbird/plugin_thunderbird.xml
index 69e83bf6df2..1adb4fa5eda 100644
--- a/addons/plugin_thunderbird/plugin_thunderbird.xml
+++ b/addons/plugin_thunderbird/plugin_thunderbird.xml
@@ -11,9 +11,6 @@
-
-
-
@@ -35,7 +32,7 @@
- Install Thunderbird Plug-In
+
diff --git a/addons/point_of_sale/__openerp__.py b/addons/point_of_sale/__openerp__.py
index 4c0bb14cc10..96d344b197b 100644
--- a/addons/point_of_sale/__openerp__.py
+++ b/addons/point_of_sale/__openerp__.py
@@ -25,6 +25,7 @@
'version': '1.0.1',
'category': 'Point Of Sale',
"sequence": 6,
+ "summary": "Touchscreen to Manage your Shop",
'description': """
This module provides a quick and easy sale process.
===================================================
diff --git a/addons/point_of_sale/i18n/point_of_sale.pot b/addons/point_of_sale/i18n/point_of_sale.pot
index b0ff45bb671..0246ca65227 100644
--- a/addons/point_of_sale/i18n/point_of_sale.pot
+++ b/addons/point_of_sale/i18n/point_of_sale.pot
@@ -155,7 +155,7 @@ msgstr ""
#. module: point_of_sale
#: report:pos.lines:0
-msgid "VAT"
+msgid "Tax"
msgstr ""
#. module: point_of_sale
diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml
index f3b491116b7..6b1b9132344 100644
--- a/addons/point_of_sale/point_of_sale_view.xml
+++ b/addons/point_of_sale/point_of_sale_view.xml
@@ -1,14 +1,15 @@
-
-
+
@@ -748,16 +749,19 @@
pos.ui
+
+
-
-
-
pos.config.form.view
pos.config
@@ -842,12 +846,12 @@
-
+ domain="[('config_id', '=', active_id)]" />
Point of Sales
@@ -864,8 +868,8 @@
id="menu_pos_config_pos"
groups="group_pos_manager"/>
- pos.session
form
-