diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py
index d771bc59506..af762611ec2 100644
--- a/addons/account/account_invoice.py
+++ b/addons/account/account_invoice.py
@@ -97,6 +97,8 @@ class account_invoice(osv.osv):
for m in invoice.move_id.line_id:
if m.account_id.type in ('receivable','payable'):
result[invoice.id] += m.amount_residual_currency
+ #prevent the residual amount on the invoice to be less than 0
+ result[invoice.id] = max(result[invoice.id], 0.0)
return result
# Give Journal Items related to the payment reconciled to this invoice
diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py
index a96ed889aae..36d211c11d2 100644
--- a/addons/account/account_move_line.py
+++ b/addons/account/account_move_line.py
@@ -741,20 +741,17 @@ class account_move_line(osv.osv):
def list_partners_to_reconcile(self, cr, uid, context=None):
cr.execute(
- """
- SELECT partner_id
- FROM (
- SELECT l.partner_id, p.last_reconciliation_date, SUM(l.debit) AS debit, SUM(l.credit) AS credit
+ """SELECT partner_id FROM (
+ SELECT l.partner_id, p.last_reconciliation_date, SUM(l.debit) AS debit, SUM(l.credit) AS credit, MAX(l.date) AS max_date
FROM account_move_line l
RIGHT JOIN account_account a ON (a.id = l.account_id)
RIGHT JOIN res_partner p ON (l.partner_id = p.id)
WHERE a.reconcile IS TRUE
AND l.reconcile_id IS NULL
- AND (p.last_reconciliation_date IS NULL OR l.date > p.last_reconciliation_date)
AND l.state <> 'draft'
GROUP BY l.partner_id, p.last_reconciliation_date
) AS s
- WHERE debit > 0 AND credit > 0
+ WHERE debit > 0 AND credit > 0 AND (last_reconciliation_date IS NULL OR max_date > last_reconciliation_date)
ORDER BY last_reconciliation_date""")
ids = cr.fetchall()
ids = len(ids) and [x[0] for x in ids] or []
diff --git a/addons/account/partner.py b/addons/account/partner.py
index 346cc38f5df..ae8d2fc1871 100644
--- a/addons/account/partner.py
+++ b/addons/account/partner.py
@@ -233,7 +233,7 @@ class res_partner(osv.osv):
help="This payment term will be used instead of the default one for purchase orders and supplier invoices"),
'ref_companies': fields.one2many('res.company', 'partner_id',
'Companies that refers to partner'),
- 'last_reconciliation_date': fields.datetime('Latest Reconciliation Date', help='Date on which the partner accounting entries were fully reconciled last time. It differs from the date of the last reconciliation made for this partner, as here we depict the fact that nothing more was to be reconciled at this date. This can be achieved in 2 ways: either the last debit/credit entry was reconciled, either the user pressed the button "Fully Reconciled" in the manual reconciliation process')
+ 'last_reconciliation_date': fields.datetime('Latest Full Reconciliation Date', help='Date on which the partner accounting entries were fully reconciled last time. It differs from the last date where a reconciliation has been made for this partner, as here we depict the fact that nothing more was to be reconciled at this date. This can be achieved in 2 different ways: either the last unreconciled debit/credit entry of this partner was reconciled, either the user pressed the button "Nothing more to reconcile" during the manual reconciliation process.')
}
res_partner()
diff --git a/addons/account/static/src/xml/account_move_reconciliation.xml b/addons/account/static/src/xml/account_move_reconciliation.xml
index d7b5301238d..3074fdb49b1 100644
--- a/addons/account/static/src/xml/account_move_reconciliation.xml
+++ b/addons/account/static/src/xml/account_move_reconciliation.xml
@@ -22,13 +22,13 @@
- Last Reconciliation:
+ Latest Manual Reconciliation Processed:
-
+
diff --git a/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py b/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py
index 254dad95c7d..8b10381434d 100644
--- a/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py
+++ b/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py
@@ -76,11 +76,11 @@ class account_analytic_account(osv.osv):
GROUP BY product_id, user_id, to_invoice, product_uom_id, line.name""", (account.id,))
res[account.id] = 0.0
- for product_id, price, user_id, factor_id, qty, uom, line_name in cr.fetchall():
+ for product_id, total_amount, user_id, factor_id, qty, uom, line_name in cr.fetchall():
#the amount to reinvoice is the real cost. We don't use the pricelist
- price = -price
+ total_amount = -total_amount
factor = self.pool.get('hr_timesheet_invoice.factor').browse(cr, uid, factor_id, context=context)
- res[account.id] += price * qty * (100 - factor.factor or 0.0) / 100.0
+ res[account.id] += total_amount * (100 - factor.factor or 0.0) / 100.0
return res
def _expense_invoiced_calc(self, cr, uid, ids, name, arg, context=None):
@@ -89,8 +89,13 @@ class account_analytic_account(osv.osv):
for account in self.browse(cr, uid, ids, context=context):
res[account.id] = 0.0
line_ids = lines_obj.search(cr, uid, [('account_id','=', account.id), ('invoice_id','!=',False), ('to_invoice','!=', False), ('journal_id.type', '=', 'purchase')], context=context)
+ #Put invoices in separate array in order not to calculate them double
+ invoices = []
for line in lines_obj.browse(cr, uid, line_ids, context=context):
- res[account.id] += line.invoice_id.amount_untaxed
+ if line.invoice_id not in invoices:
+ invoices.append(line.invoice_id)
+ for invoice in invoices:
+ res[account.id] += invoice.amount_untaxed
return res
def _ca_invoiced_calc(self, cr, uid, ids, name, arg, context=None):
diff --git a/addons/auth_oauth/controllers/main.py b/addons/auth_oauth/controllers/main.py
index 1c38f7f4eae..3f7ef2f3f0c 100644
--- a/addons/auth_oauth/controllers/main.py
+++ b/addons/auth_oauth/controllers/main.py
@@ -64,8 +64,13 @@ class OAuthController(oeweb.Controller):
u = registry.get('res.users')
credentials = u.auth_oauth(cr, SUPERUSER_ID, provider, kw, context=context)
cr.commit()
- action = state.get('a', None)
- url = '/#action=' + action if action else '/'
+ action = state.get('a')
+ menu = state.get('m')
+ url = '/'
+ if action:
+ url = '/#action=%s' % action
+ elif menu:
+ url = '/#menu_id=%s' % menu
return login_and_redirect(req, *credentials, redirect_url=url)
except AttributeError:
# auth_signup is not installed
diff --git a/addons/base_action_rule/base_action_rule_view.xml b/addons/base_action_rule/base_action_rule_view.xml
index 4e551fa21c5..5eaaf08dad4 100644
--- a/addons/base_action_rule/base_action_rule_view.xml
+++ b/addons/base_action_rule/base_action_rule_view.xml
@@ -27,8 +27,8 @@
-
-
+
+
diff --git a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create_view.xml b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create_view.xml
index 74d0fa1daf6..17ac669d26d 100644
--- a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create_view.xml
+++ b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create_view.xml
@@ -9,16 +9,17 @@
@@ -724,10 +719,6 @@
string="Partial"
type="action" states="confirmed,assigned"
icon="gtk-justify-fill"/>
-
diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py
index af5d7aa663c..7e8487b2b2b 100644
--- a/addons/point_of_sale/point_of_sale.py
+++ b/addons/point_of_sale/point_of_sale.py
@@ -89,7 +89,7 @@ class pos_config(osv.osv):
'sequence_id' : False,
}
d.update(default)
- return super(pos_order, self).copy(cr, uid, id, d, context=context)
+ return super(pos_config, self).copy(cr, uid, id, d, context=context)
def name_get(self, cr, uid, ids, context=None):
diff --git a/addons/point_of_sale/wizard/pos_box.py b/addons/point_of_sale/wizard/pos_box.py
index 14e653b915c..776d962a3a8 100644
--- a/addons/point_of_sale/wizard/pos_box.py
+++ b/addons/point_of_sale/wizard/pos_box.py
@@ -31,6 +31,10 @@ class PosBoxIn(PosBox):
_inherit = 'cash.box.in'
def _compute_values_for_statement_line(self, cr, uid, box, record, context=None):
+
+ if context is None:
+ context = {}
+
values = super(PosBoxIn, self)._compute_values_for_statement_line(cr, uid, box, record, context=context)
active_model = context.get('active_model', False) or False
diff --git a/addons/portal_anonymous/static/src/js/portal_anonymous.js b/addons/portal_anonymous/static/src/js/portal_anonymous.js
index a719a285908..d4f6351efc9 100644
--- a/addons/portal_anonymous/static/src/js/portal_anonymous.js
+++ b/addons/portal_anonymous/static/src/js/portal_anonymous.js
@@ -43,8 +43,12 @@ openerp.portal_anonymous = function(instance) {
instance.web.Login.include({
start: function() {
var self = this;
+ var anonymous_mode = (!self.session.session_is_valid() && !(self.params.token || self.params.login));
+ if (anonymous_mode) {
+ self.$el.hide();
+ }
return $.when(this._super()).then(function() {
- if (!self.session.session_is_valid() && !(self.params.token || self.params.login)) {
+ if (anonymous_mode) {
self.remember_credentials = false;
// XXX get login/pass from server (via a rpc call) ?
return self.do_login(self.selected_db, 'anonymous', 'anonymous');
diff --git a/addons/portal_project_long_term/__init__.py b/addons/portal_project_long_term/__init__.py
new file mode 100644
index 00000000000..c8d82ed19bd
--- /dev/null
+++ b/addons/portal_project_long_term/__init__.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2010-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 .
+#
+##############################################################################
diff --git a/addons/portal_project_long_term/__openerp__.py b/addons/portal_project_long_term/__openerp__.py
new file mode 100644
index 00000000000..44156afbc06
--- /dev/null
+++ b/addons/portal_project_long_term/__openerp__.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2010-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 .
+#
+##############################################################################
+
+
+{
+ 'name': 'Portal Project Long Term',
+ 'version': '1.0',
+ 'category': 'Tools',
+ 'complexity': 'easy',
+ 'description': """
+This module adds necessary security rules and access rights for project long term and portal.
+=============================================================================================
+ """,
+ 'author': 'OpenERP SA',
+ 'depends': ['project_long_term', 'portal'],
+ 'data': [
+ 'security/portal_security.xml',
+ 'security/ir.model.access.csv',
+ ],
+ 'installable': True,
+ 'auto_install': True,
+ 'category': 'Hidden',
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/portal_project_long_term/security/ir.model.access.csv b/addons/portal_project_long_term/security/ir.model.access.csv
new file mode 100644
index 00000000000..738c264a71c
--- /dev/null
+++ b/addons/portal_project_long_term/security/ir.model.access.csv
@@ -0,0 +1,2 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_issues,project_phase,project_long_term.model_project_phase,portal.group_portal,1,0,0,0
diff --git a/addons/portal_project_long_term/security/portal_security.xml b/addons/portal_project_long_term/security/portal_security.xml
new file mode 100644
index 00000000000..b46d277e4ee
--- /dev/null
+++ b/addons/portal_project_long_term/security/portal_security.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ Portal Personal Long term project
+
+ ['|',('project_id.message_follower_ids','in', [user.partner_id.id]),('task_ids.message_follower_ids','in', [user.partner_id.id])]
+
+
+
+
+
+
+
+
+
diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py
index 9233a95634c..01dab2962f3 100644
--- a/addons/project_issue/project_issue.py
+++ b/addons/project_issue/project_issue.py
@@ -392,10 +392,18 @@ class project_issue(base_stage, osv.osv):
context=context)
def write(self, cr, uid, ids, vals, context=None):
- #Update last action date every time the user change the stage, the state or send a new email
- logged_fields = ['stage_id', 'state', 'message_ids']
- if any([field in vals for field in logged_fields]):
- vals['date_action_last'] = time.strftime('%Y-%m-%d %H:%M:%S')
+
+ #Update last action date every time the user changes the stage
+ if 'stage_id' in vals:
+ vals['date_action_last'] = time.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
+ state = self.pool.get('project.task.type').browse(cr, uid, vals['stage_id'], context=context).state
+ for issue in self.browse(cr, uid, ids, context=context):
+ # Change from draft to not draft EXCEPT cancelled: The issue has been opened -> set the opening date
+ if issue.state == 'draft' and state not in ('draft', 'cancelled'):
+ vals['date_open'] = time.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
+ # Change from not done to done: The issue has been closed -> set the closing date
+ if issue.state != 'done' and state == 'done':
+ vals['date_closed'] = time.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
return super(project_issue, self).write(cr, uid, ids, vals, context)
@@ -545,6 +553,19 @@ class project_issue(base_stage, osv.osv):
return super(project_issue, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context)
+ def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification', subtype=None, parent_id=False, attachments=None, context=None, content_subtype='html', **kwargs):
+ """ Overrides mail_thread message_post so that we can set the date of last action field when
+ a new message is posted on the issue.
+ """
+ if context is None:
+ context = {}
+
+ res = super(project_issue, self).message_post(cr, uid, thread_id, body=body, subject=subject, type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, content_subtype=content_subtype, **kwargs)
+
+ if thread_id:
+ self.write(cr, uid, thread_id, {'date_action_last': time.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
+
+ return res
class project(osv.osv):
_inherit = "project.project"
diff --git a/addons/sale/report/sale_order.py b/addons/sale/report/sale_order.py
index b4f0b1589aa..134e87a0c2e 100644
--- a/addons/sale/report/sale_order.py
+++ b/addons/sale/report/sale_order.py
@@ -27,9 +27,18 @@ class order(report_sxw.rml_parse):
def __init__(self, cr, uid, name, context=None):
super(order, self).__init__(cr, uid, name, context=context)
self.localcontext.update({
- 'time': time,
+ 'time': time,
+ 'show_discount':self._show_discount,
})
+ def _show_discount(self, uid, context=None):
+ cr = self.cr
+ try:
+ group_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'group_discount_per_so_line')[1]
+ except:
+ return False
+ return group_id in [x.id for x in self.pool.get('res.users').browse(cr, uid, uid, context=context).groups_id]
+
report_sxw.report_sxw('report.sale.order', 'sale.order', 'addons/sale/report/sale_order.rml', parser=order, header="external")
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/sale/report/sale_order.rml b/addons/sale/report/sale_order.rml
index a00d2d4e3ef..130220535f9 100644
--- a/addons/sale/report/sale_order.rml
+++ b/addons/sale/report/sale_order.rml
@@ -251,7 +251,7 @@
Unit Price
- Disc.(%)
+ [[not show_discount(user.id) and removeParentNode('para') ]]Disc.(%)
- [[ formatLang(line.discount, digits=get_digits(dp='Discount'))]]
+ [[show_discount(user.id) and formatLang(line.discount, digits=get_digits(dp='Discount')) or '']]
[[ formatLang(line.price_subtotal, digits=get_digits(dp='Account'), currency_obj=o.pricelist_id.currency_id) ]]
diff --git a/addons/sale/report/sale_report.py b/addons/sale/report/sale_report.py
index a20ba01f300..070e9c4336f 100644
--- a/addons/sale/report/sale_report.py
+++ b/addons/sale/report/sale_report.py
@@ -88,12 +88,11 @@ class sale_report(osv.osv):
s.project_id as analytic_account_id
from
sale_order s
- left join sale_order_line l on (s.id=l.order_id)
+ join sale_order_line l on (s.id=l.order_id)
left join product_product p on (l.product_id=p.id)
left join product_template t on (p.product_tmpl_id=t.id)
left join product_uom u on (u.id=l.product_uom)
left join product_uom u2 on (u2.id=t.uom_id)
- where l.product_id is not null
group by
l.product_id,
l.product_uom_qty,
diff --git a/addons/sale_stock/report/sale_report.py b/addons/sale_stock/report/sale_report.py
index ade84b269e6..1f1e4a625be 100644
--- a/addons/sale_stock/report/sale_report.py
+++ b/addons/sale_stock/report/sale_report.py
@@ -68,8 +68,8 @@ class sale_report(osv.osv):
s.project_id as analytic_account_id
from
sale_order s
- left join sale_order_line l on (s.id=l.order_id)
- left join product_product p on (l.product_id=p.id)
+ join sale_order_line l on (s.id=l.order_id)
+ left join product_product p on (l.product_id=p.id)
left join product_template t on (p.product_tmpl_id=t.id)
left join product_uom u on (u.id=l.product_uom)
left join product_uom u2 on (u2.id=t.uom_id)
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index 6bf020623f7..ba3db96ff68 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -19,7 +19,6 @@
#
##############################################################################
-from lxml import etree
from datetime import datetime
from dateutil.relativedelta import relativedelta
import time
@@ -704,29 +703,29 @@ class stock_picking(osv.osv):
default = {}
default = default.copy()
picking_obj = self.browse(cr, uid, id, context=context)
- move_obj=self.pool.get('stock.move')
- if ('name' not in default) or (picking_obj.name=='/'):
- seq_obj_name = 'stock.picking.' + picking_obj.type
+ move_obj = self.pool.get('stock.move')
+ if ('name' not in default) or (picking_obj.name == '/'):
+ seq_obj_name = 'stock.picking.' + picking_obj.type
default['name'] = self.pool.get('ir.sequence').get(cr, uid, seq_obj_name)
default['origin'] = ''
default['backorder_id'] = False
if 'invoice_state' not in default and picking_obj.invoice_state == 'invoiced':
default['invoice_state'] = '2binvoiced'
- res=super(stock_picking, self).copy(cr, uid, id, default, context)
+ res = super(stock_picking, self).copy(cr, uid, id, default, context)
if res:
picking_obj = self.browse(cr, uid, res, context=context)
for move in picking_obj.move_lines:
- move_obj.write(cr, uid, [move.id], {'tracking_id': False,'prodlot_id':False, 'move_history_ids2': [(6, 0, [])], 'move_history_ids': [(6, 0, [])]})
+ move_obj.write(cr, uid, [move.id], {'tracking_id': False, 'prodlot_id': False, 'move_history_ids2': [(6, 0, [])], 'move_history_ids': [(6, 0, [])]})
return res
-
+
def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
if view_type == 'form' and not view_id:
mod_obj = self.pool.get('ir.model.data')
if self._name == "stock.picking.in":
- model,view_id = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_in_form')
+ model, view_id = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_in_form')
if self._name == "stock.picking.out":
- model,view_id = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_out_form')
- return super(stock_picking,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
+ model, view_id = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_out_form')
+ return super(stock_picking, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
def onchange_partner_in(self, cr, uid, ids, partner_id=None, context=None):
return {}
@@ -2440,9 +2439,8 @@ class stock_move(osv.osv):
context = {}
ctx = context.copy()
for move in self.browse(cr, uid, ids, context=context):
- if move.state != 'draft' and not ctx.get('call_unlink',False):
- raise osv.except_osv(_('User Error!'),
- _('You can only delete draft moves.'))
+ if move.state != 'draft' and not ctx.get('call_unlink', False):
+ raise osv.except_osv(_('User Error!'), _('You can only delete draft moves.'))
return super(stock_move, self).unlink(
cr, uid, ids, context=ctx)
@@ -2470,13 +2468,20 @@ class stock_move(osv.osv):
raise osv.except_osv(_('Warning!'), _('Please provide a positive quantity to scrap.'))
res = []
for move in self.browse(cr, uid, ids, context=context):
+ source_location = move.location_id
+ if move.state == 'done':
+ source_location = move.location_dest_id
+ if source_location.usage != 'internal':
+ #restrict to scrap from a virtual location because it's meaningless and it may introduce errors in stock ('creating' new products from nowhere)
+ raise osv.except_osv(_('Error!'), _('Forbidden operation: it is not allowed to scrap products from a virtual location.'))
move_qty = move.product_qty
uos_qty = quantity / move_qty * move.product_uos_qty
default_val = {
+ 'location_id': source_location.id,
'product_qty': quantity,
'product_uos_qty': uos_qty,
'state': move.state,
- 'scrapped' : True,
+ 'scrapped': True,
'location_dest_id': location_id,
'tracking_id': move.tracking_id.id,
'prodlot_id': move.prodlot_id.id,
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index bc47d83dc89..6162d073fde 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -1129,7 +1129,7 @@
stock.move
-
+
diff --git a/addons/subscription/subscription.py b/addons/subscription/subscription.py
index d181e97e8f0..92bde135bc6 100644
--- a/addons/subscription/subscription.py
+++ b/addons/subscription/subscription.py
@@ -85,6 +85,17 @@ class subscription_subscription(osv.osv):
'state': lambda *a: 'draft'
}
+ def _auto_end(self, cr, context=None):
+ super(subscription_subscription, self)._auto_end(cr, context=context)
+ # drop the FK from subscription to ir.cron, as it would cause deadlocks
+ # during cron job execution. When model_copy() tries to write() on the subscription,
+ # it has to wait for an ExclusiveLock on the cron job record, but the latter
+ # is locked by the cron system for the duration of the job!
+ # FIXME: the subscription module should be reviewed to simplify the scheduling process
+ # and to use a unique cron job for all subscriptions, so that it never needs to
+ # be updated during its execution.
+ cr.execute("ALTER TABLE %s DROP CONSTRAINT %s" % (self._table, '%s_cron_id_fkey' % self._table))
+
def set_process(self, cr, uid, ids, context=None):
for row in self.read(cr, uid, ids, context=context):
mapping = {'name':'name','interval_number':'interval_number','interval_type':'interval_type','exec_init':'numbercall','date_init':'nextcall'}