diff --git a/addons/account_analytic_analysis/__init__.py b/addons/account_analytic_analysis/__init__.py index a7bdb11ad67..429aa011f31 100644 --- a/addons/account_analytic_analysis/__init__.py +++ b/addons/account_analytic_analysis/__init__.py @@ -20,6 +20,7 @@ ############################################################################## import account_analytic_analysis +import cron_account_analytic_account # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account_analytic_analysis/__openerp__.py b/addons/account_analytic_analysis/__openerp__.py index f75fe3d1615..f9bda19d405 100644 --- a/addons/account_analytic_analysis/__openerp__.py +++ b/addons/account_analytic_analysis/__openerp__.py @@ -42,6 +42,7 @@ user-wise as well as month wise. "security/ir.model.access.csv", "account_analytic_analysis_view.xml", "account_analytic_analysis_menu.xml", + "account_analytic_analysis_cron.xml", ], 'demo_xml' : [], 'installable': True, diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 22eb9401ad7..8ebb75244a1 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -374,7 +374,28 @@ class account_analytic_account(osv.osv): res[id] = round(res.get(id, 0.0),2) return res - _columns ={ + def _is_overdue_quantity(self, cr, uid, ids, fieldnames, args, context=None): + result = dict.fromkeys(ids, 0) + + for record in self.browse(cr, uid, ids, context=context): + if record.quantity == 0.0 and record.quantity_max == 0.0: + result[record.id] = 0 + else: + result[record.id] = int(record.quantity >= record.quantity_max) + + return result + + def _get_analytic_account(self, cr, uid, ids, context=None): + result = set() + for line in self.pool.get('account.analytic.line').browse(cr, uid, ids, context=context): + result.add(line.account_id.id) + return list(result) + + _columns = { + 'is_overdue_quantity' : fields.function(_is_overdue_quantity, method=True, type='boolean', string='Overdue Quantity', + store={ + 'account.analytic.line' : (_get_analytic_account, None, 20), + }), 'ca_invoiced': fields.function(_ca_invoiced_calc, type='float', string='Invoiced Amount', help="Total customer invoiced amount for this account.", digits_compute=dp.get_precision('Account')), diff --git a/addons/account_analytic_analysis/account_analytic_analysis_cron.xml b/addons/account_analytic_analysis/account_analytic_analysis_cron.xml new file mode 100644 index 00000000000..8564b1606eb --- /dev/null +++ b/addons/account_analytic_analysis/account_analytic_analysis_cron.xml @@ -0,0 +1,15 @@ + + + + + Analytic Account Report for Sales + 1 + weeks + -1 + + + + + + + diff --git a/addons/account_analytic_analysis/account_analytic_analysis_menu.xml b/addons/account_analytic_analysis/account_analytic_analysis_menu.xml index bfb7a892668..34840814154 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis_menu.xml +++ b/addons/account_analytic_analysis/account_analytic_analysis_menu.xml @@ -20,6 +20,67 @@ tree,form,graph [('date','<=',time.strftime('%Y-%m-%d')),('state','=','open')] - + + + + account.analytic.account.search + account.analytic.account + search + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Overdue Accounts + account.analytic.account + form + tree,form,graph + {'search_default_has_partner':1, 'search_default_my_accounts':1, 'search_default_draft':1, 'search_default_pending':1, 'search_default_open':1, 'search_default_renew':1} + [('type','=','normal')] + + You will find here the contracts to be renewed because the deadline is passed or the working hours are higher than the allocated hours. OpenERP automatically sets these analytic accounts to the pending state, in order to raise a warning during the timesheets recording. Salesmen should review all pending accounts and reopen or close the according to the negotiation with the customer. + + + diff --git a/addons/account_analytic_analysis/cron_account_analytic_account.py b/addons/account_analytic_analysis/cron_account_analytic_account.py new file mode 100644 index 00000000000..581bbfe92e1 --- /dev/null +++ b/addons/account_analytic_analysis/cron_account_analytic_account.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +from osv import osv +from mako.template import Template +import time +try: + import cStringIO as StringIO +except ImportError: + import StringIO + +import tools + +MAKO_TEMPLATE = u"""Hello ${user.name}, + +Here is a list of contracts that have to be renewed for two +possible reasons: + - the end of contract date is passed + - the customer consumed more hours than expected + +Can you contact the customer in order to sell a new or renew its contract. +The contract has been set with a pending state, can you update the status +of the analytic account following this rule: + - Set Done: if the customer does not want to renew + - Set Open: if the customer purchased an extra contract + +Here is the list of contracts to renew: +% for partner, accounts in partners.iteritems(): + * ${partner.name} + % for account in accounts: + - Name: ${account.name} + % if account.quantity_max != 0.0: + - Quantity: ${account.quantity}/${account.quantity_max} hours + % endif + - Dates: ${account.date_start} to ${account.date and account.date or '???'} + - Contacts: + % for address in account.partner_id.address: + . ${address.name}, ${address.phone}, ${address.email} + % endfor + + % endfor +% endfor + +You can use the report in the menu: Sales > Invoicing > Overdue Accounts + +Regards, + +-- +OpenERP +""" + +class analytic_account(osv.osv): + _inherit = 'account.analytic.account' + + def cron_account_analytic_account(self, cr, uid, context=None): + domain = [ + ('name', 'not ilike', 'maintenance'), + ('partner_id', '!=', False), + ('user_id', '!=', False), + ('user_id.user_email', '!=', False), + ('state', 'in', ('draft', 'open')), + '|', ('date', '<', time.strftime('%Y-%m-%d')), ('date', '=', False), + ] + + account_ids = self.search(cr, uid, domain, context=context, order='name asc') + accounts = self.browse(cr, uid, account_ids, context=context) + + users = dict() + for account in accounts: + users.setdefault(account.user_id, dict()).setdefault(account.partner_id, []).append(account) + + account.write({'state' : 'pending'}, context=context) + + for user, data in users.iteritems(): + subject = '[OPENERP] Reporting: Analytic Accounts' + body = Template(MAKO_TEMPLATE).render_unicode(user=user, partners=data) + tools.email_send('noreply@openerp.com', [user.user_email, ], subject, body) + + return True + +analytic_account() diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 805c35e4462..4b3fe0a167d 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -446,15 +446,11 @@ the rule to mark CC(mail to any other person defined in actions)."), scrit = [] for action in self.browse(cr, uid, ids, context=context): - model_obj = self.pool.get(action.model_id.model) for obj in objects: - ok = self.do_check(cr, uid, action, obj, context=context) - if not ok: - continue + if self.do_check(cr, uid, action, obj, context=context): + model_obj = self.pool.get(action.model_id.model) + self.do_action(cr, uid, action, model_obj, obj, context=context) - if ok: - self.do_action(cr, uid, action, model_obj, obj, context) - break context.update({'action': False}) return True diff --git a/addons/crm/crm.py b/addons/crm/crm.py index ac0874a6037..d12860b5ea5 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -486,8 +486,11 @@ class crm_case(crm_base): src = case_email dest = case.user_id.user_email or "" body = case.description or "" - if case.message_ids: - body = case.message_ids[0].body_text or "" + for message in case.message_ids: + if message.email_from: + body = message.description + break + if not destination: src, dest = dest, case.email_from if body and case.user_id.signature: diff --git a/addons/crm/crm_action_rule.py b/addons/crm/crm_action_rule.py index 559d257820a..3947eb58156 100644 --- a/addons/crm/crm_action_rule.py +++ b/addons/crm/crm_action_rule.py @@ -92,7 +92,6 @@ class base_action_rule(osv.osv): def do_action(self, cr, uid, action, model_obj, obj, context=None): res = super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context) write = {} - if hasattr(action, 'act_section_id') and action.act_section_id: obj.section_id = action.act_section_id write['section_id'] = action.act_section_id.id @@ -110,6 +109,7 @@ class base_action_rule(osv.osv): model_obj.message_append(cr, uid, [obj], _(action.act_state)) model_obj.write(cr, uid, [obj.id], write, context) + super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context) emails = [] if hasattr(obj, 'email_from') and action.act_mail_to_partner: diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 621eef59da5..b8d038b15c3 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -74,7 +74,7 @@ class mail_message_common(osv.osv_memory): 'model': fields.char('Related Document model', size=128, select=1, readonly=1), 'res_id': fields.integer('Related Document ID', select=1, readonly=1), 'date': fields.datetime('Date'), - 'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences'), + 'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences. If empty, this is not a mail but a message.'), 'email_to': fields.char('To', size=256, help='Message recipients'), 'email_cc': fields.char('Cc', size=256, help='Carbon copy message recipients'), 'email_bcc': fields.char('Bcc', size=256, help='Blind carbon copy message recipients'), diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index a67d60b5890..54e7dd22cdf 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -98,26 +98,25 @@ class project_issue(crm.crm_case, osv.osv): ans = False hours = 0 + date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S") if field in ['working_hours_open','day_open']: if issue.date_open: - date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S") date_open = datetime.strptime(issue.date_open, "%Y-%m-%d %H:%M:%S") ans = date_open - date_create date_until = issue.date_open #Calculating no. of working hours to open the issue hours = cal_obj.interval_hours_get(cr, uid, issue.project_id.resource_calendar_id.id, - datetime.strptime(issue.create_date, '%Y-%m-%d %H:%M:%S'), - datetime.strptime(issue.date_open, '%Y-%m-%d %H:%M:%S')) + date_create, + date_open) elif field in ['working_hours_close','day_close']: if issue.date_closed: - date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S") date_close = datetime.strptime(issue.date_closed, "%Y-%m-%d %H:%M:%S") date_until = issue.date_closed ans = date_close - date_create #Calculating no. of working hours to close the issue hours = cal_obj.interval_hours_get(cr, uid, issue.project_id.resource_calendar_id.id, - datetime.strptime(issue.create_date, '%Y-%m-%d %H:%M:%S'), - datetime.strptime(issue.date_closed, '%Y-%m-%d %H:%M:%S')) + date_create, + date_close) elif field in ['days_since_creation']: if issue.create_date: days_since_creation = datetime.today() - datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S") @@ -139,7 +138,11 @@ class project_issue(crm.crm_case, osv.osv): duration = float(ans.days) if issue.project_id and issue.project_id.resource_calendar_id: duration = float(ans.days) * 24 - new_dates = cal_obj.interval_min_get(cr, uid, issue.project_id.resource_calendar_id.id, datetime.strptime(issue.create_date, '%Y-%m-%d %H:%M:%S'), duration, resource=resource_id) + + new_dates = cal_obj.interval_min_get(cr, uid, + issue.project_id.resource_calendar_id.id, + date_create, + duration, resource=resource_id) no_days = [] date_until = datetime.strptime(date_until, '%Y-%m-%d %H:%M:%S') for in_time, out_time in new_dates: @@ -148,10 +151,12 @@ class project_issue(crm.crm_case, osv.osv): if out_time > date_until: break duration = len(no_days) + if field in ['working_hours_open','working_hours_close']: res[issue.id][field] = hours else: res[issue.id][field] = abs(float(duration)) + return res def _get_issue_task(self, cr, uid, ids, context=None): @@ -365,7 +370,6 @@ class project_issue(crm.crm_case, osv.osv): self.write(cr, uid, task.id, {'type_id': index and types[index-1] or False}) return True - def onchange_task_id(self, cr, uid, ids, task_id, context=None): result = {} if not task_id: @@ -383,7 +387,7 @@ class project_issue(crm.crm_case, osv.osv): """ cases = self.browse(cr, uid, ids) for case in cases: - data = {} + data = {'state' : 'draft'} if case.project_id.project_escalation_id: data['project_id'] = case.project_id.project_escalation_id.id if case.project_id.project_escalation_id.user_id: diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index e064f0c8c8f..a1602113979 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -55,7 +55,7 @@ - + @@ -188,7 +188,7 @@