diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py index 0dd075d48e6..e709fb6b7a6 100644 --- a/addons/crm/__openerp__.py +++ b/addons/crm/__openerp__.py @@ -81,6 +81,8 @@ Dashboard for CRM will include: 'crm_lead_view.xml', 'crm_lead_menu.xml', + 'crm_salesteams.xml', + 'crm_meeting_menu.xml', 'crm_phonecall_view.xml', @@ -114,6 +116,8 @@ Dashboard for CRM will include: 'test/ui/duplicate_lead.yml', 'test/ui/delete_lead.yml', ], + 'css': ['static/src/css/crm.css'], + 'js': ['static/src/js/crm.js'], 'installable': True, 'application': True, 'auto_install': False, diff --git a/addons/crm/crm.py b/addons/crm/crm.py index df6d5e12b66..00e7b425dd6 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -109,6 +109,19 @@ class crm_case_section(osv.osv): def get_full_name(self, cr, uid, ids, field_name, arg, context=None): return dict(self.name_get(cr, uid, ids, context=context)) + def get_number_items(self, cr, uid, ids, model, domain, context=None): + res = dict.fromkeys(ids, 0) + obj = self.pool.get(model) + for section_id in ids: + res[section_id] = obj.search(cr, uid, [("section_id", "=", section_id)] + domain, count=True, context=context) + return res + + def _get_number_leads(self, cr, uid, ids, field_name, arg, context=None): + return self.get_number_items(cr, uid, ids, 'crm.lead', ['|', '|', ("type", "=", "lead"), ("type", "=", "both"), ("type", "=", False), ('state', 'not in', ['done', 'cancel'])], context=context) + + def _get_number_opportunities(self, cr, uid, ids, field_name, arg, context=None): + return self.get_number_items(cr, uid, ids, 'crm.lead', ['|', ("type", "=", "opportunity"), ("type", "=", "both"), ('state', 'not in', ['done', 'cancel'])], context=context) + _columns = { 'name': fields.char('Sales Team', size=64, required=True, translate=True), 'complete_name': fields.function(get_full_name, type='char', size=256, readonly=True, store=True), @@ -128,6 +141,9 @@ class crm_case_section(osv.osv): 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True, help="The email address associated with this team. New emails received will automatically " "create new leads assigned to the team."), + 'number_lead': fields.function(_get_number_leads, type='integer', readonly=True), + 'number_opportunity': fields.function(_get_number_opportunities, type='integer', readonly=True), + 'color': fields.integer('Color Index'), } def _get_stage_common(self, cr, uid, context): @@ -137,7 +153,6 @@ class crm_case_section(osv.osv): _defaults = { 'active': 1, 'stage_ids': _get_stage_common, - 'alias_domain': False, # always hide alias during creation } _sql_constraints = [ diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 6c74ead0140..55e8b89e5dc 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -84,6 +84,20 @@ class crm_lead(base_stage, format_address, osv.osv): }, } + def dynamic_help(self, cr, uid, help, context=None): + if context.get('default_type', None) == 'lead': + context['dynamic_help_model'] = 'crm.case.section' + context['dynamic_help_id'] = context.get('default_section_id', None) + context['dynamic_help_documents'] = _("leads") + return super(crm_lead, self).dynamic_help(cr, uid, help, context=context) + + def onchange_user_id(self, cr, uid, ids, section_id, user_id, context=None): + if user_id: + section_ids = self.pool.get('crm.case.section').search(cr, uid, ['|', ('user_id', '=', user_id), ('member_ids', '=', user_id)], context=context) + if section_ids and (not section_id or section_id not in section_ids): + section_id = section_ids[0] + return {'value': {'section_id': section_id}} + def create(self, cr, uid, vals, context=None): if context is None: context = {} diff --git a/addons/crm/crm_lead_data.xml b/addons/crm/crm_lead_data.xml index e335bd1b2ff..1d47660c57d 100644 --- a/addons/crm/crm_lead_data.xml +++ b/addons/crm/crm_lead_data.xml @@ -45,6 +45,7 @@ Won + done diff --git a/addons/crm/crm_lead_menu.xml b/addons/crm/crm_lead_menu.xml index 8c38ed41a1c..c51d5aecd0c 100644 --- a/addons/crm/crm_lead_menu.xml +++ b/addons/crm/crm_lead_menu.xml @@ -9,11 +9,12 @@ ['|', ('type','=','lead'), ('type','=',False)] - {'default_type':'lead', 'stage_type':'lead'} + { + 'default_type':'lead', + 'stage_type':'lead' + } -

- Click to create an unqualified lead. -

+

Use leads if you need a qualification step before creating an opportunity or a customer. It can be a business card you received, a contact form filled in your website, or a file of unqualified @@ -44,7 +45,11 @@ crm.lead kanban,tree,graph,form,calendar [('type','=','opportunity')] - {'stage_type': 'opportunity', 'default_type': 'opportunity', 'default_user_id': uid} + { + 'stage_type': 'opportunity', + 'default_type': 'opportunity', + 'default_user_id': uid + } @@ -82,9 +87,9 @@ - - - + + + diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index df28d240a0c..6fdee2d42e5 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -151,7 +151,7 @@ --> -

@@ -427,7 +427,7 @@ - +
- - - -
diff --git a/addons/crm/res_partner_view.xml b/addons/crm/res_partner_view.xml index 00a071fa954..150c1eeebbe 100644 --- a/addons/crm/res_partner_view.xml +++ b/addons/crm/res_partner_view.xml @@ -30,8 +30,11 @@ crm.lead kanban,tree,form,graph,calendar [('type','=','opportunity')] - {'search_default_partner_id': active_id, - 'stage_type': 'opportunity', 'default_type': 'opportunity'} + { + 'search_default_partner_id': active_id, + 'stage_type': 'opportunity', + 'default_type': 'opportunity' + } diff --git a/addons/crm/security/ir.model.access.csv b/addons/crm/security/ir.model.access.csv index 74394d3c5ce..eaa11433b1b 100644 --- a/addons/crm/security/ir.model.access.csv +++ b/addons/crm/security/ir.model.access.csv @@ -36,4 +36,4 @@ access_res_partner_bank_type_crm_user,res.partner.bank.type.crm.user,base.model_ access_crm_lead_partner_manager,crm.lead.partner.manager,model_crm_lead,base.group_partner_manager,1,0,0,0 access_crm_phonecall_partner_manager,crm.phonecall.partner.manager,model_crm_phonecall,base.group_partner_manager,1,1,1,1 access_crm_payment_mode_user,crm.payment.mode,model_crm_payment_mode,base.group_sale_salesman,1,0,0,0 -access_crm_payment_mode,crm.payment.mode,model_crm_payment_mode,base.group_sale_manager,1,1,1,1 +access_crm_payment_mode,crm.payment.mode,model_crm_payment_mode,base.group_sale_manager,1,1,1,1 \ No newline at end of file diff --git a/addons/crm/static/src/css/crm.css b/addons/crm/static/src/css/crm.css new file mode 100644 index 00000000000..25494a761b5 --- /dev/null +++ b/addons/crm/static/src/css/crm.css @@ -0,0 +1,41 @@ +.oe_kanban_crm_salesteams { + width: 220px; + min-height: 160px; +} +.oe_kanban_crm_salesteams_avatars { + margin-top: 8px; +} + +.oe_kanban_crm_salesteams_avatars img { + width: 30px; + height: 30px; + padding-left: 0px; + margin-top: 3px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); + -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); +} + +.oe_kanban_crm_salesteams_alias { + margin: -8px 0 8px 0; +} + +.oe_kanban_crm_salesteams_alias .oe_e { + font-size: 30px; + line-height: 6px; + vertical-align: top; + margin-right: 3px; + color: white; + text-shadow: 0px 0px 2px rgba(0, 0, 0, 1); + float: left; +} + +.oe_kanban_crm_salesteams_list a { + white-space: nowrap; +} +.oe_kanban_crm_salesteams_list a:hover { + text-decoration: underline !important; +} \ No newline at end of file diff --git a/addons/crm/static/src/js/crm.js b/addons/crm/static/src/js/crm.js new file mode 100644 index 00000000000..e027313d6dc --- /dev/null +++ b/addons/crm/static/src/js/crm.js @@ -0,0 +1,11 @@ +openerp.crm = function(openerp) { + openerp.web_kanban.KanbanRecord.include({ + on_card_clicked: function() { + if (this.view.dataset.model === 'crm.case.section') { + this.$('.oe_kanban_crm_salesteams_list a').first().click(); + } else { + this._super.apply(this, arguments); + } + }, + }); +}; diff --git a/addons/crm_claim/__init__.py b/addons/crm_claim/__init__.py index 2012c227adf..7395ea83fe5 100644 --- a/addons/crm_claim/__init__.py +++ b/addons/crm_claim/__init__.py @@ -21,7 +21,6 @@ import crm_claim import report -import res_config # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm_claim/__openerp__.py b/addons/crm_claim/__openerp__.py index 78dfba2ed53..61257265f1b 100644 --- a/addons/crm_claim/__openerp__.py +++ b/addons/crm_claim/__openerp__.py @@ -41,7 +41,6 @@ automatically new claims based on incoming emails. 'crm_claim_menu.xml', 'security/ir.model.access.csv', 'report/crm_claim_report_view.xml', - 'res_config_view.xml', 'crm_claim_data.xml', ], 'demo': ['crm_claim_demo.xml'], diff --git a/addons/crm_claim/res_config.py b/addons/crm_claim/res_config.py deleted file mode 100644 index aa0cb7ffa20..00000000000 --- a/addons/crm_claim/res_config.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Business Applications -# Copyright (C) 2004-2012 OpenERP S.A. (). -# -# 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 . -# -############################################################################## - -from openerp.osv import fields, osv - -class crm_claim_settings(osv.osv_memory): - _name = 'sale.config.settings' - _inherit = ['sale.config.settings', 'fetchmail.config.settings'] - - _columns = { - 'fetchmail_claim': fields.boolean("Create claims from incoming mails", - fetchmail_model='crm.claim', fetchmail_name='Incoming Claims', - help="""Allows you to configure your incoming mail server, and create claims from incoming emails."""), - } diff --git a/addons/crm_claim/res_config_view.xml b/addons/crm_claim/res_config_view.xml deleted file mode 100644 index 79474f1fb00..00000000000 --- a/addons/crm_claim/res_config_view.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - crm_claim settings - sale.config.settings - - - -
-
- -
-
-
-
- -
-
diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml index e0c3edfdb39..ccd11bc0dc2 100644 --- a/addons/hr/hr_view.xml +++ b/addons/hr/hr_view.xml @@ -341,7 +341,8 @@ - + +
- -
- -
+ + +
diff --git a/addons/mail/doc/mail_alias.rst b/addons/mail/doc/mail_alias.rst new file mode 100644 index 00000000000..fa5a2fe0fda --- /dev/null +++ b/addons/mail/doc/mail_alias.rst @@ -0,0 +1,37 @@ +.. _mail_alias: + +mail.alias +============ + +Models ++++++++ + +``mail.alias`` is a class for mapping of an email address with a given OpenERP Document model. It is used by OpenERP's mail gateway when processing incoming emails sent to the system. If the recipient address (To) of the message matches a Mail Alias, the message will be either processed following the rules of that alias. If the message is a reply it will be attached to the existing discussion on the corresponding record, otherwise a new record of the corresponding model will be created. +This is meant to be used in combination with a catch-all email configuration on the company's mail server, so that as soon as a new mail.alias is created, it becomes immediately usable and OpenERP will accept email for it. + +.. versionchanged:: 7.1 + +Some Fields ++++++++++++ + + - ``alias_name`` : + The name of the email alias, e.g. 'jobs' + - ``alias_model_id`` : + The model (OpenERP Document Kind) to which this alias corresponds. Any incoming email that does not reply to an existing record will cause the creation of a new record of this model (e.g. a Project Task) + - ``alias_defaults`` : + A Python dictionary that will be evaluated to provide default values when creating new records for this alias. + - ``alias_domain`` : + +Methods ++++++++ + + - ``name_get`` : + Return the mail alias display alias_name, inclusing the implicit mail catchall domain from config. + e.g. `jobs@openerp.my.openerp.com` or `sales@openerp.my.openerp.com` + - ``create_unique_alias`` : + Creates an email.alias record according to the values provided in ``vals``, with 2 alterations: the ``alias_name`` value may be suffixed in order to make it unique, and the ``alias_model_id`` value will set to the model ID of the ``model_name`` value, if provided, + - ``get_alias`` : + Return the mail alias for a document (or the default mail alias of the model). + Arguments: + model (model OpenERP) + alias_defaults (A Python dictionary to provide default values when creating new records for this alias.) \ No newline at end of file diff --git a/addons/mail/mail_alias.py b/addons/mail/mail_alias.py index d9c49d833ab..a079448487d 100644 --- a/addons/mail/mail_alias.py +++ b/addons/mail/mail_alias.py @@ -22,6 +22,7 @@ import logging import re import unicodedata +import ast from openerp.osv import fields, osv from openerp.tools import ustr @@ -117,8 +118,13 @@ class mail_alias(osv.Model): mail catchall domain from config. e.g. `jobs@openerp.my.openerp.com` or `sales@openerp.my.openerp.com` """ - return [(record['id'], "%s@%s" % (record['alias_name'], record['alias_domain'])) - for record in self.read(cr, uid, ids, ['alias_name', 'alias_domain'], context=context)] + res = [] + for record in self.browse(cr, uid, ids, context=context): + if record.alias_name and record.alias_domain: + res.append((record['id'], "%s@%s" % (record.alias_name, record.alias_domain))) + else: + res.append((record['id'], False)) + return res def _find_unique(self, cr, uid, name, context=None): """Find a unique alias name similar to ``name``. If ``name`` is diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index 77c0b8f6c86..d103199d567 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -69,39 +69,39 @@
- +

- -
-
+
- - - - - - - -
- This group is visible by everyone, - including your customers if you installed - the portal module. -
-
- Only the invited followers can read the - discussions on this group. -
-
+ + + + + + +
+ This group is visible by everyone, + including your customers if you installed + the portal module. +
+
+ Only the invited followers can read the + discussions on this group. +
diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index b8bd028289f..c37a22d88f5 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -89,6 +89,28 @@ class mail_thread(osv.AbstractModel): # :param function lambda: returns whether the tracking should record using this subtype _track = {} + def dynamic_help(self, cr, uid, help, context=None): + if not context.get('dynamic_help_model', None): + if context.get('dynamic_help_id', None): + object_id = self.pool.get(context.get('dynamic_help_model')).browse(cr, uid, context.get('dynamic_help_id'), context=context) + alias = object_id.alias_id and object_id.alias_id.name_get() or False + if alias and alias[0] and alias[0][1]: + email = alias[0][1] + return "%s %s" % (_("

Click here to add a new %s or send an email to: %s

") % (context.get('dynamic_help_documents', _("documents")), email, email), help or "") + else: + model_id = self.pool.get('ir.model').search(cr, uid, [("model", "=", self._name)], context=context)[0] + alias_obj = self.pool.get('mail.alias') + alias_ids = alias_obj.search(cr, uid, [("alias_model_id", "=", model_id)], context=context, limit=5) + if alias_ids: + for alias in alias_obj.browse(cr, uid, alias_ids, context=context): + email = "%s@%s" % (alias.alias_name, alias.alias_domain) + return "%s %s" % (_("

Click here to add a new %s or send an email, for example, to: %s

") % (context.get('dynamic_help_documents', _("documents")), email, email), help or "") + + if context.get('dynamic_help_documents', None) and ("%s" % help).find("oe_view_nocontent_create") == -1: + return "%s %s" % (_("

Click here to add a new %s

") % context.get('dynamic_help_documents', _("documents")), help or "") + + return help + def _get_message_data(self, cr, uid, ids, name, args, context=None): """ Computes: - message_unread: has uid unread message for the document diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 51306727a09..e54a2d0bc4a 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -4,7 +4,6 @@ * 'button' element wich comes with a lot of inappropriate * styling. Entypo is also a headache to center properly * */ - .openerp .oe_topbar_item.oe_topbar_compose_full_email{ padding: 0px; width: 32px; diff --git a/addons/mail/static/src/css/mail_group.css b/addons/mail/static/src/css/mail_group.css index c1cefee9070..4f2b8a16dd0 100644 --- a/addons/mail/static/src/css/mail_group.css +++ b/addons/mail/static/src/css/mail_group.css @@ -14,6 +14,10 @@ padding: 0px 18px; } +.openerp .oe_form_sheetbg.openerp_mail_group_sheet .oe_avatar { + padding-top: 3px; +} + /* Resize footer width */ .openerp .oe_form div.oe_mail_group_footer { max-width: 80%; @@ -21,7 +25,7 @@ /* Resize group description */ .openerp .oe_form_sheetbg.openerp_mail_group_sheet .oe_form_field_text > textarea { - height: 40px; + height: 60px; } /* ------------------------------ */ diff --git a/addons/mail/tests/__init__.py b/addons/mail/tests/__init__.py index b945da54655..5d581e40276 100644 --- a/addons/mail/tests/__init__.py +++ b/addons/mail/tests/__init__.py @@ -19,10 +19,11 @@ # ############################################################################## -from . import test_mail_message, test_mail_features, test_message_read, test_invite +from . import test_mail_message, test_mail_features, test_message_read, test_invite, test_mail_alias checks = [ test_mail_message, + test_mail_alias, test_mail_features, test_message_read, test_invite, diff --git a/addons/mail/tests/test_mail_alias.py b/addons/mail/tests/test_mail_alias.py new file mode 100644 index 00000000000..2bd3e345a49 --- /dev/null +++ b/addons/mail/tests/test_mail_alias.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2012-TODAY OpenERP S.A. +# +# 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 . +# +############################################################################## + +from openerp.tools import mute_logger +from openerp.tests import common + +class test_mail_alias(common.TransactionCase): + + def setUp(self): + super(test_mail_alias, self).setUp() + cr, uid = self.cr, self.uid + + # Usefull models + self.ir_model = self.registry('ir.model') + self.mail_alias = self.registry('mail.alias') + + #@mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm') + def test_00_mail_alias(self): + """ Testing mail_group access rights and basic mail_thread features """ + cr, uid = self.cr, self.uid + + alias_name_0 = "global+alias+test_0" + alias_name_1 = "document+alias+test_1" + alias_defaults_1 = {'field_pigs': 11} + alias_name_2 = "document+alias+test_2" + alias_defaults_2 = {'field_pigs': 112} + + # Create an alias + partner_model_id = self.ir_model.search(cr, uid, [('model', '=', 'mail.alias')])[0] + alias_id_0 = self.mail_alias.create(cr, uid, + {'alias_model_id': partner_model_id, 'alias_name': alias_name_0, 'alias_defaults': {}}) + alias_id_1 = self.mail_alias.create(cr, uid, + {'alias_model_id': partner_model_id, 'alias_name': alias_name_1, 'alias_defaults': alias_defaults_1}) + alias_id_2 = self.mail_alias.create(cr, uid, + {'alias_model_id': partner_model_id, 'alias_name': alias_name_2, 'alias_defaults': alias_defaults_2}) \ No newline at end of file diff --git a/addons/project/project.py b/addons/project/project.py index 0751865d9c4..770a986d1b6 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -286,7 +286,6 @@ class project(osv.osv): 'type_ids': _get_type_common, 'alias_model': 'project.task', 'privacy_visibility': 'public', - 'alias_domain': False, # always hide alias during creation } # TODO: Why not using a SQL contraints ? @@ -531,11 +530,11 @@ def Project(): context = dict(context, project_creation_in_progress=True) mail_alias = self.pool.get('mail.alias') if not vals.get('alias_id') and vals.get('name', False): - vals.pop('alias_name', None) # prevent errors during copy() + alias_name = vals.pop('alias_name', None) # prevent errors during copy() alias_id = mail_alias.create_unique_alias(cr, uid, # Using '+' allows using subaddressing for those who don't # have a catchall domain setup. - {'alias_name': "project+"+short_name(vals['name'])}, + {'alias_name': alias_name or "project+"+short_name(vals['name'])}, model_name=vals.get('alias_model', 'project.task'), context=context) vals['alias_id'] = alias_id @@ -605,8 +604,7 @@ class task(base_stage, osv.osv): search_domain = [] project_id = self._resolve_project_id_from_context(cr, uid, context=context) if project_id: - search_domain += ['|', ('project_ids', '=', project_id)] - search_domain += [('id', 'in', ids)] + search_domain += [('project_ids', '=', project_id)] stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context) result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) # restore order of the search @@ -887,8 +885,16 @@ class task(base_stage, osv.osv): for f in res['fields']: if 'Hours' in res['fields'][f]['string']: res['fields'][f]['string'] = res['fields'][f]['string'].replace('Hours',tm) + return res + + def dynamic_help(self, cr, uid, help, context=None): + context['dynamic_help_model'] = 'project.project' + context['dynamic_help_id'] = context.get('default_project_id', None) + context['dynamic_help_documents'] = _("tasks") + return super(task, self).dynamic_help(cr, uid, help, context=context) + # ---------------------------------------- # Case management # ---------------------------------------- diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 16dbe44ccc1..42fe3a1df12 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -50,12 +50,13 @@ Tasks project.task kanban,tree,form,calendar,gantt,graph - {'search_default_project_id': [active_id], 'default_project_id': active_id} + { + 'search_default_project_id': [active_id], + 'default_project_id': active_id, + } -

- Click to create a new task. -

+

OpenERP's project management allows you to manage the pipeline of tasks in order to get things done efficiently. You can track progress, discuss on tasks, attach documents, etc. @@ -111,6 +112,15 @@

+ + @@ -133,13 +143,6 @@ - -
- -
-
@@ -231,7 +234,6 @@ - @@ -245,8 +247,10 @@
-

+
+ %% +
Tasks @@ -622,9 +626,7 @@ kanban,tree,form,calendar,gantt,graph -

- Click to create a new task. -

+

OpenERP's project management allows you to manage the pipeline of tasks in order to get things done efficiently. You can track progress, discuss on tasks, attach documents, etc. diff --git a/addons/project/static/src/css/project.css b/addons/project/static/src/css/project.css index 1ea3c705105..e4d2196d476 100644 --- a/addons/project/static/src/css/project.css +++ b/addons/project/static/src/css/project.css @@ -62,4 +62,21 @@ -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); } +.oe_kanban_project_alias { + margin: -8px 0 8px 0; +} +.oe_kanban_project_alias .oe_e { + font-size: 30px; + line-height: 6px; + vertical-align: top; + margin-right: 3px; + color: white; + text-shadow: 0px 0px 2px rgba(0, 0, 0, 1); + float: left; +} + +.oe_kanban_project_alias small { + display: block; + margin-left: 20px; +} \ No newline at end of file diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 9233a95634c..e89c8379a69 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -412,6 +412,12 @@ class project_issue(base_stage, osv.osv): self.write(cr, uid, ids, {'date_open': False, 'date_closed': False}) return res + def dynamic_help(self, cr, uid, help, context=None): + context['dynamic_help_model'] = 'project.project' + context['dynamic_help_id'] = context.get('default_project_id', None) + context['dynamic_help_documents'] = _("issues") + return super(project_issue, self).dynamic_help(cr, uid, help, context=context) + # ------------------------------------------------------- # Stage management # ------------------------------------------------------- diff --git a/addons/project_issue/project_issue_menu.xml b/addons/project_issue/project_issue_menu.xml index 78841f56156..ad08edaab2b 100644 --- a/addons/project_issue/project_issue_menu.xml +++ b/addons/project_issue/project_issue_menu.xml @@ -14,9 +14,7 @@ -

- Click to report a new issue. -

+

The OpenERP issues tacker allows you to efficiantly manage things like internal requests, software development bugs, customer complaints, project troubles, material breakdowns, etc. diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index 500a1376422..5dc0106eb85 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -302,7 +302,19 @@ form Issues kanban,tree,form,calendar,graph - {'search_default_project_id': [active_id], 'default_project_id': active_id} + { + 'search_default_project_id': [active_id], + 'default_project_id': active_id + } + +

+ Click to report a new issue. +

+ The OpenERP issues tacker allows you to efficiantly manage things + like internal requests, software development bugs, customer + complaints, project troubles, material breakdowns, etc. +

+
@@ -324,9 +336,10 @@ - - diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index e3a393666b4..138e682b32f 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -340,6 +340,7 @@

+ diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index 1f3f85d5ed1..ef4b51154e7 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -29,6 +29,23 @@ class sale_order(osv.osv): domain="['|',('section_id','=',section_id),('section_id','=',False), ('object_id.model', '=', 'crm.lead')]") } +class crm_case_section(osv.osv): + _inherit = 'crm.case.section' + + def _get_number_saleorder(self, cr, uid, ids, field_name, arg, context=None): + return self.get_number_items(cr, uid, ids, 'sale.order', [('state','not in',('draft','sent','cancel'))], context=context) + + def _get_number_quotation(self, cr, uid, ids, field_name, arg, context=None): + return self.get_number_items(cr, uid, ids, 'sale.order', [('state','in',('draft','sent','cancel'))], context=context) + + def _get_number_invoice(self, cr, uid, ids, field_name, arg, context=None): + return self.get_number_items(cr, uid, ids, 'account.invoice', [('state','not in',('draft','cancel'))], context=context) + + _columns = { + 'number_saleorder': fields.function(_get_number_saleorder, type='integer', readonly=True), + 'number_quotation': fields.function(_get_number_quotation, type='integer', readonly=True), + 'number_invoice': fields.function(_get_number_invoice, type='integer', readonly=True), + } class res_users(osv.Model): _inherit = 'res.users' diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index 0b5fce2f5cd..f428dee7b63 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -113,5 +113,93 @@
+ + + + Sales Orders + ir.actions.act_window + sale.order + form + tree,form,calendar,graph + + [('state','not in',('draft','sent','cancel'))] + { + 'search_default_section_id': [active_id], 'default_section_id': active_id + } + +

+ Click to create a quotation that can be converted into a sales + order. +

+ OpenERP will help you efficiently handle the complete sales flow: + quotation, sales order, delivery, invoicing and payment. +

+
+
+ + + Quotations + ir.actions.act_window + sale.order + form + + tree,form,calendar,graph + { + 'search_default_section_id': [active_id], 'default_section_id': active_id, 'show_address': 1 + } + [('state','in',('draft','sent','cancel'))] + + +

+ Click to create a quotation, the first step of a new sale. +

+ OpenERP will help you handle efficiently the complete sale flow: + from the quotation to the sales order, the + delivery, the invoicing and the payment collection. +

+ The social feature helps you organize discussions on each sales + order, and allow your customers to keep track of the evolution + of the sales order. +

+
+
+ + + Invoices + account.invoice + form + tree,form,calendar,graph + + [('state','not in',['draft','cancel'])] + {'search_default_section_id': [active_id], 'default_section_id': active_id} + + + + + crm.case.section.kanban + crm.case.section + + + + + + + + + +
+ + QuotationQuotations + + + Sales OrderSales Orders + + + InvoiceInvoices + + + + +