"
total = reduce(lambda x, y: x+y['balance'], currency_dict['line'], 0.00)
@@ -434,15 +437,17 @@ class res_partner(osv.osv):
_columns = {
'payment_responsible_id':fields.many2one('res.users', ondelete='set null', string='Follow-up Responsible',
help="Optionally you can assign a user to this field, which will make him responsible for the action.",
- track_visibility="onchange"),
- 'payment_note':fields.text('Customer Payment Promise', help="Payment Note", track_visibility="onchange"),
- 'payment_next_action':fields.text('Next Action',
+ track_visibility="onchange", copy=False),
+ 'payment_note':fields.text('Customer Payment Promise', help="Payment Note", track_visibility="onchange", copy=False),
+ 'payment_next_action':fields.text('Next Action', copy=False,
help="This is the next action to be taken. It will automatically be set when the partner gets a follow-up level that requires a manual action. ",
track_visibility="onchange"),
- 'payment_next_action_date':fields.date('Next Action Date',
- help="This is when the manual follow-up is needed. " \
- "The date will be set to the current date when the partner gets a follow-up level that requires a manual action. "\
- "Can be practical to set manually e.g. to see if he keeps his promises."),
+ 'payment_next_action_date': fields.date('Next Action Date', copy=False,
+ help="This is when the manual follow-up is needed. "
+ "The date will be set to the current date when the partner "
+ "gets a follow-up level that requires a manual action. "
+ "Can be practical to set manually e.g. to see if he keeps "
+ "his promises."),
'unreconciled_aml_ids':fields.one2many('account.move.line', 'partner_id', domain=['&', ('reconcile_id', '=', False), '&',
('account_id.active','=', True), '&', ('account_id.type', '=', 'receivable'), ('state', '!=', 'draft')]),
'latest_followup_date':fields.function(_get_latest, method=True, type='date', string="Latest Follow-up Date",
diff --git a/addons/account_followup/report/account_followup_print.py b/addons/account_followup/report/account_followup_print.py
index 4e856853915..bf20527d3fa 100644
--- a/addons/account_followup/report/account_followup_print.py
+++ b/addons/account_followup/report/account_followup_print.py
@@ -75,9 +75,7 @@ class report_rappel(report_sxw.rml_parse):
return [{'line': lines} for lines in lines_per_currency.values()]
def _get_text(self, stat_line, followup_id, context=None):
- if context is None:
- context = {}
- context.update({'lang': stat_line.partner_id.lang})
+ context = dict(context or {}, lang=stat_line.partner_id.lang)
fp_obj = self.pool['account_followup.followup']
fp_line = fp_obj.browse(self.cr, self.uid, followup_id, context=context).followup_line
if not fp_line:
diff --git a/addons/account_followup/tests/test_account_followup.py b/addons/account_followup/tests/test_account_followup.py
index b3025021278..697fb433362 100644
--- a/addons/account_followup/tests/test_account_followup.py
+++ b/addons/account_followup/tests/test_account_followup.py
@@ -42,7 +42,7 @@ class TestAccountFollowup(TransactionCase):
'quantity': 5,
'price_unit':200
})]})
- self.registry('account.invoice').signal_invoice_open(cr, uid, [self.invoice_id])
+ self.registry('account.invoice').signal_workflow(cr, uid, [self.invoice_id], 'invoice_open')
self.voucher = self.registry("account.voucher")
@@ -112,8 +112,7 @@ class TestAccountFollowup(TransactionCase):
partner_rec = self.partner.browse(cr, uid, self.partner_id)
self.run_wizard_three_times()
self.partner.action_done(cr, uid, self.partner_id)
- self.assertEqual(partner_rec.payment_next_action,
- "", "Manual action not emptied")
+ self.assertFalse(partner_rec.payment_next_action, "Manual action not emptied")
self.assertFalse(partner_rec.payment_responsible_id)
self.assertFalse(partner_rec.payment_next_action_date)
diff --git a/addons/account_followup/wizard/account_followup_print.py b/addons/account_followup/wizard/account_followup_print.py
index 7b7cb8fc77e..83100723edb 100644
--- a/addons/account_followup/wizard/account_followup_print.py
+++ b/addons/account_followup/wizard/account_followup_print.py
@@ -204,15 +204,14 @@ class account_followup_print(osv.osv_memory):
return len(partners_to_clear)
def do_process(self, cr, uid, ids, context=None):
- if context is None:
- context = {}
+ context = dict(context or {})
#Get partners
tmp = self._get_partners_followp(cr, uid, ids, context=context)
partner_list = tmp['partner_ids']
to_update = tmp['to_update']
date = self.browse(cr, uid, ids, context=context)[0].date
- data = self.read(cr, uid, ids, [], context=context)[0]
+ data = self.read(cr, uid, ids, context=context)[0]
data['followup_id'] = data['followup_id'][0]
#Update partners
diff --git a/addons/account_payment/account_payment.py b/addons/account_payment/account_payment.py
index d2b2786e995..de72960a9c6 100644
--- a/addons/account_payment/account_payment.py
+++ b/addons/account_payment/account_payment.py
@@ -87,13 +87,13 @@ class payment_order(osv.osv):
_columns = {
'date_scheduled': fields.date('Scheduled Date', states={'done':[('readonly', True)]}, help='Select a date if you have chosen Preferred Date to be fixed.'),
- 'reference': fields.char('Reference', required=1, states={'done': [('readonly', True)]}),
+ 'reference': fields.char('Reference', required=1, states={'done': [('readonly', True)]}, copy=False),
'mode': fields.many2one('payment.mode', 'Payment Mode', select=True, required=1, states={'done': [('readonly', True)]}, help='Select the Payment Mode to be applied.'),
'state': fields.selection([
('draft', 'Draft'),
('cancel', 'Cancelled'),
('open', 'Confirmed'),
- ('done', 'Done')], 'Status', select=True,
+ ('done', 'Done')], 'Status', select=True, copy=False,
help='When an order is placed the status is \'Draft\'.\n Once the bank is confirmed the status is set to \'Confirmed\'.\n Then the order is paid the status is \'Done\'.'),
'line_ids': fields.one2many('payment.line', 'order_id', 'Payment lines', states={'done': [('readonly', True)]}),
'total': fields.function(_total, string="Total", type='float'),
@@ -132,19 +132,9 @@ class payment_order(osv.osv):
def set_done(self, cr, uid, ids, *args):
self.write(cr, uid, ids, {'date_done': time.strftime('%Y-%m-%d')})
- self.signal_done(cr, uid, [ids[0]])
+ self.signal_workflow(cr, uid, ids, 'done')
return True
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default.update({
- 'state': 'draft',
- 'line_ids': [],
- 'reference': self.pool.get('ir.sequence').get(cr, uid, 'payment.order')
- })
- return super(payment_order, self).copy(cr, uid, id, default, context=context)
-
def write(self, cr, uid, ids, vals, context=None):
if context is None:
context = {}
@@ -179,7 +169,7 @@ class payment_line(osv.osv):
"due_date": "date_maturity",
"reference": "ref"}.get(orig, orig)
- def info_owner(self, cr, uid, ids, name=None, args=None, context=None):
+ def _info_owner(self, cr, uid, ids, name=None, args=None, context=None):
result = {}
for line in self.browse(cr, uid, ids, context=context):
owner = line.order_id.mode.bank_id.partner_id
@@ -197,7 +187,7 @@ class payment_line(osv.osv):
cntry = partner_record.country_id and partner_record.country_id.name or ''
return partner_record.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
- def info_partner(self, cr, uid, ids, name=None, args=None, context=None):
+ def _info_partner(self, cr, uid, ids, name=None, args=None, context=None):
result = {}
for line in self.browse(cr, uid, ids, context=context):
result[line.id] = False
@@ -322,8 +312,8 @@ class payment_line(osv.osv):
type='date', help="Invoice Effective Date"),
'ml_maturity_date': fields.function(_get_ml_maturity_date, type='date', string='Due Date'),
'ml_inv_ref': fields.function(_get_ml_inv_ref, type='many2one', relation='account.invoice', string='Invoice Ref.'),
- 'info_owner': fields.function(info_owner, string="Owner Account", type="text", help='Address of the Main Partner'),
- 'info_partner': fields.function(info_partner, string="Destination Account", type="text", help='Address of the Ordering Customer.'),
+ 'info_owner': fields.function(_info_owner, string="Owner Account", type="text", help='Address of the Main Partner'),
+ 'info_partner': fields.function(_info_partner, string="Destination Account", type="text", help='Address of the Ordering Customer.'),
'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"),
'create_date': fields.datetime('Created', readonly=True),
'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True),
diff --git a/addons/account_payment/test/payment_order_process.yml b/addons/account_payment/test/payment_order_process.yml
index 2bb04807a64..5aaf97b9e71 100644
--- a/addons/account_payment/test/payment_order_process.yml
+++ b/addons/account_payment/test/payment_order_process.yml
@@ -79,7 +79,7 @@
-
I create a bank statement.
-
- !record {model: account.bank.statement, id: account_bank_statement_1}:
+ !record {model: account.bank.statement, id: account_bank_statement_1, view: False}:
balance_end_real: 0.0
balance_start: 0.0
date: !eval time.strftime('%Y-%m-%d')
diff --git a/addons/account_payment/wizard/account_payment_order.py b/addons/account_payment/wizard/account_payment_order.py
index 8d5df048fe0..78b634d44fc 100644
--- a/addons/account_payment/wizard/account_payment_order.py
+++ b/addons/account_payment/wizard/account_payment_order.py
@@ -107,7 +107,7 @@ class payment_order_create(osv.osv_memory):
domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('credit', '>', 0), ('account_id.reconcile', '=', True)]
domain = domain + ['|', ('date_maturity', '<=', search_due_date), ('date_maturity', '=', False)]
line_ids = line_obj.search(cr, uid, domain, context=context)
- context.update({'line_ids': line_ids})
+ context = dict(context, line_ids=line_ids)
model_data_ids = mod_obj.search(cr, uid,[('model', '=', 'ir.ui.view'), ('name', '=', 'view_create_payment_order_lines')], context=context)
resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id']
return {'name': _('Entry Lines'),
diff --git a/addons/account_payment/wizard/account_payment_populate_statement.py b/addons/account_payment/wizard/account_payment_populate_statement.py
index ef068a55b2c..6a049ff78a9 100644
--- a/addons/account_payment/wizard/account_payment_populate_statement.py
+++ b/addons/account_payment/wizard/account_payment_populate_statement.py
@@ -62,7 +62,7 @@ class account_payment_populate_statement(osv.osv_memory):
if context is None:
context = {}
- data = self.read(cr, uid, ids, [], context=context)[0]
+ data = self.read(cr, uid, ids, context=context)[0]
line_ids = data['lines']
if not line_ids:
return {'type': 'ir.actions.act_window_close'}
@@ -77,7 +77,7 @@ class account_payment_populate_statement(osv.osv_memory):
if not line.move_line_id.id:
continue
- context.update({'move_line_ids': [line.move_line_id.id]})
+ context = dict(context, move_line_ids=[line.move_line_id.id])
result = voucher_obj.onchange_partner_id(cr, uid, [], partner_id=line.partner_id.id, journal_id=statement.journal_id.id, amount=abs(amount), currency_id= statement.currency.id, ttype='payment', date=line.ml_maturity_date, context=context)
if line.move_line_id:
diff --git a/addons/account_sequence/account_sequence.py b/addons/account_sequence/account_sequence.py
index 1b60e51e124..c85320d92c3 100644
--- a/addons/account_sequence/account_sequence.py
+++ b/addons/account_sequence/account_sequence.py
@@ -26,7 +26,9 @@ class account_move(osv.osv):
_inherit = 'account.move'
_columns = {
- 'internal_sequence_number': fields.char('Internal Number', readonly=True, help='Internal Sequence Number'),
+ 'internal_sequence_number': fields.char('Internal Number',
+ readonly=True, copy=False,
+ help='Internal Sequence Number'),
}
def post(self, cr, uid, ids, context=None):
diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py
index 9fde90231b2..275e002695e 100644
--- a/addons/account_voucher/account_voucher.py
+++ b/addons/account_voucher/account_voucher.py
@@ -335,10 +335,13 @@ class account_voucher(osv.osv):
('receipt','Receipt'),
],'Default Type', readonly=True, states={'draft':[('readonly',False)]}),
'name':fields.char('Memo', readonly=True, states={'draft':[('readonly',False)]}),
- 'date':fields.date('Date', readonly=True, select=True, states={'draft':[('readonly',False)]}, help="Effective date for accounting entries"),
+ 'date':fields.date('Date', readonly=True, select=True, states={'draft':[('readonly',False)]},
+ help="Effective date for accounting entries", copy=False),
'journal_id':fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}),
'account_id':fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}),
- 'line_ids':fields.one2many('account.voucher.line','voucher_id','Voucher Lines', readonly=True, states={'draft':[('readonly',False)]}),
+ 'line_ids':fields.one2many('account.voucher.line', 'voucher_id', 'Voucher Lines',
+ readonly=True, copy=True,
+ states={'draft':[('readonly',False)]}),
'line_cr_ids':fields.one2many('account.voucher.line','voucher_id','Credits',
domain=[('type','=','cr')], context={'default_type':'cr'}, readonly=True, states={'draft':[('readonly',False)]}),
'line_dr_ids':fields.one2many('account.voucher.line','voucher_id','Debits',
@@ -352,16 +355,17 @@ class account_voucher(osv.osv):
('cancel','Cancelled'),
('proforma','Pro-forma'),
('posted','Posted')
- ], 'Status', readonly=True, track_visibility='onchange',
+ ], 'Status', readonly=True, track_visibility='onchange', copy=False,
help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed Voucher. \
\n* The \'Pro-forma\' when voucher is in Pro-forma status,voucher does not have an voucher number. \
\n* The \'Posted\' status is used when user create voucher,a voucher number is generated and voucher entries are created in account \
\n* The \'Cancelled\' status is used when user cancel voucher.'),
'amount': fields.float('Total', digits_compute=dp.get_precision('Account'), required=True, readonly=True, states={'draft':[('readonly',False)]}),
'tax_amount':fields.float('Tax Amount', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}),
- 'reference': fields.char('Ref #', readonly=True, states={'draft':[('readonly',False)]}, help="Transaction reference number."),
- 'number': fields.char('Number', readonly=True,),
- 'move_id':fields.many2one('account.move', 'Account Entry'),
+ 'reference': fields.char('Ref #', readonly=True, states={'draft':[('readonly',False)]},
+ help="Transaction reference number.", copy=False),
+ 'number': fields.char('Number', readonly=True, copy=False),
+ 'move_id':fields.many2one('account.move', 'Account Entry', copy=False),
'move_ids': fields.related('move_id','line_id', type='one2many', relation='account.move.line', string='Journal Items', readonly=True),
'partner_id':fields.many2one('res.partner', 'Partner', change_default=1, readonly=True, states={'draft':[('readonly',False)]}),
'audit': fields.related('move_id','to_check', type='boolean', help='Check this box if you are unsure of that journal entry and if you want to note it as \'to be reviewed\' by an accounting expert.', relation='account.move', string='To Review'),
@@ -573,7 +577,7 @@ class account_voucher(osv.osv):
ctx.update({'date': date})
#read the voucher rate with the right date in the context
currency_id = currency_id or self.pool.get('res.company').browse(cr, uid, company_id, context=ctx).currency_id.id
- voucher_rate = self.pool.get('res.currency').read(cr, uid, currency_id, ['rate'], context=ctx)['rate']
+ voucher_rate = self.pool.get('res.currency').read(cr, uid, [currency_id], ['rate'], context=ctx)[0]['rate']
ctx.update({
'voucher_special_currency': payment_rate_currency_id,
'voucher_special_currency_rate': rate * voucher_rate})
@@ -615,7 +619,7 @@ class account_voucher(osv.osv):
'payment_rate_currency_id': payment_rate_currency_id
})
#read the voucher rate with the right date in the context
- voucher_rate = self.pool.get('res.currency').read(cr, uid, currency_id, ['rate'], context=ctx)['rate']
+ voucher_rate = self.pool.get('res.currency').read(cr, uid, [currency_id], ['rate'], context=ctx)[0]['rate']
ctx.update({
'voucher_special_currency_rate': payment_rate * voucher_rate,
'voucher_special_currency': payment_rate_currency_id})
@@ -847,7 +851,7 @@ class account_voucher(osv.osv):
ctx = context.copy()
ctx.update({'date': date})
#read the voucher rate with the right date in the context
- voucher_rate = self.pool.get('res.currency').read(cr, uid, currency_id, ['rate'], context=ctx)['rate']
+ voucher_rate = self.pool.get('res.currency').read(cr, uid, [currency_id], ['rate'], context=ctx)[0]['rate']
ctx.update({
'voucher_special_currency_rate': payment_rate * voucher_rate,
'voucher_special_currency': payment_rate_currency_id})
@@ -922,7 +926,7 @@ class account_voucher(osv.osv):
return vals
def button_proforma_voucher(self, cr, uid, ids, context=None):
- self.signal_proforma_voucher(cr, uid, ids)
+ self.signal_workflow(cr, uid, ids, 'proforma_voucher')
return {'type': 'ir.actions.act_window_close'}
def proforma_voucher(self, cr, uid, ids, context=None):
@@ -1182,7 +1186,7 @@ class account_voucher(osv.osv):
tot_line = line_total
rec_lst_ids = []
- date = self.read(cr, uid, voucher_id, ['date'], context=context)['date']
+ date = self.read(cr, uid, [voucher_id], ['date'], context=context)[0]['date']
ctx = context.copy()
ctx.update({'date': date})
voucher = self.pool.get('account.voucher').browse(cr, uid, voucher_id, context=ctx)
@@ -1419,22 +1423,6 @@ class account_voucher(osv.osv):
reconcile = move_line_pool.reconcile_partial(cr, uid, rec_ids, writeoff_acc_id=voucher.writeoff_acc_id.id, writeoff_period_id=voucher.period_id.id, writeoff_journal_id=voucher.journal_id.id)
return True
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default.update({
- 'state': 'draft',
- 'number': False,
- 'move_id': False,
- 'line_cr_ids': False,
- 'line_dr_ids': False,
- 'reference': False
- })
- if 'date' not in default:
- default['date'] = time.strftime('%Y-%m-%d')
- return super(account_voucher, self).copy(cr, uid, id, default, context)
-
-
class account_voucher_line(osv.osv):
_name = 'account.voucher.line'
_description = 'Voucher Lines'
@@ -1495,7 +1483,7 @@ class account_voucher_line(osv.osv):
'reconcile': fields.boolean('Full Reconcile'),
'type':fields.selection([('dr','Debit'),('cr','Credit')], 'Dr/Cr'),
'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'),
- 'move_line_id': fields.many2one('account.move.line', 'Journal Item'),
+ 'move_line_id': fields.many2one('account.move.line', 'Journal Item', copy=False),
'date_original': fields.related('move_line_id','date', type='date', relation='account.move.line', string='Date', readonly=1),
'date_due': fields.related('move_line_id','date_maturity', type='date', relation='account.move.line', string='Due Date', readonly=1),
'amount_original': fields.function(_compute_balance, multi='dc', type='float', string='Original Amount', store=True, digits_compute=dp.get_precision('Account')),
diff --git a/addons/account_voucher/test/case1_usd_usd.yml b/addons/account_voucher/test/case1_usd_usd.yml
index 44269104370..84ae118ab16 100644
--- a/addons/account_voucher/test/case1_usd_usd.yml
+++ b/addons/account_voucher/test/case1_usd_usd.yml
@@ -182,7 +182,7 @@
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 1 USD/USD'), ('partner_id', '=', ref('base.res_partner_19'))])
- self.signal_proforma_voucher(cr, uid, voucher)
+ self.signal_workflow(cr, uid, voucher, 'proforma_voucher')
-
I check that the move of my first voucher is valid
-
@@ -276,7 +276,7 @@
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 1'), ('partner_id', '=', ref('base.res_partner_19'))])
- self.signal_proforma_voucher(cr, uid, voucher)
+ self.signal_workflow(cr, uid, voucher, 'proforma_voucher')
-
I check that the move of my second voucher is valid
-
diff --git a/addons/account_voucher/test/case1_usd_usd_payment_rate.yml b/addons/account_voucher/test/case1_usd_usd_payment_rate.yml
index 037fc1497ec..c0aa617783c 100644
--- a/addons/account_voucher/test/case1_usd_usd_payment_rate.yml
+++ b/addons/account_voucher/test/case1_usd_usd_payment_rate.yml
@@ -191,7 +191,7 @@
-
!python {model: account.voucher}: |
voucher = ref('account_voucher_1_case1_payment_rate')
- self.signal_proforma_voucher(cr, uid, [voucher])
+ self.signal_workflow(cr, uid, [voucher], 'proforma_voucher')
-
I check that the move of my first voucher is valid
-
diff --git a/addons/account_voucher/test/case2_suppl_usd_eur.yml b/addons/account_voucher/test/case2_suppl_usd_eur.yml
index c13bfcbd5d4..836bee35d30 100644
--- a/addons/account_voucher/test/case2_suppl_usd_eur.yml
+++ b/addons/account_voucher/test/case2_suppl_usd_eur.yml
@@ -161,7 +161,7 @@
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_19'))])
- self.signal_proforma_voucher(cr, uid, voucher)
+ self.signal_workflow(cr, uid, voucher, 'proforma_voucher')
-
I check that the move of my voucher is valid
-
@@ -263,7 +263,7 @@
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_19'))])
- self.signal_proforma_voucher(cr, uid, voucher)
+ self.signal_workflow(cr, uid, voucher, 'proforma_voucher')
-
I check that my voucher state is posted
-
diff --git a/addons/account_voucher/test/case2_usd_eur_debtor_in_eur.yml b/addons/account_voucher/test/case2_usd_eur_debtor_in_eur.yml
index f523e312933..45c86eb7ad0 100644
--- a/addons/account_voucher/test/case2_usd_eur_debtor_in_eur.yml
+++ b/addons/account_voucher/test/case2_usd_eur_debtor_in_eur.yml
@@ -182,7 +182,7 @@
!python {model: account.voucher}: |
from openerp import netsvc
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 USD/EUR DR EUR'), ('partner_id', '=', ref('base.res_partner_19'))])
- self.signal_proforma_voucher(cr, uid, voucher)
+ self.signal_workflow(cr, uid, voucher, 'proforma_voucher')
-
I check that the move of my voucher is valid
-
@@ -257,7 +257,7 @@
!python {model: account.voucher}: |
from openerp import netsvc
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR EUR'), ('partner_id', '=', ref('base.res_partner_19'))])
- self.signal_proforma_voucher(cr, uid, voucher)
+ self.signal_workflow(cr, uid, voucher, 'proforma_voucher')
-
I check that my voucher state is posted
-
diff --git a/addons/account_voucher/test/case2_usd_eur_debtor_in_usd.yml b/addons/account_voucher/test/case2_usd_eur_debtor_in_usd.yml
index 52c416e06cc..f353cd8c6fc 100644
--- a/addons/account_voucher/test/case2_usd_eur_debtor_in_usd.yml
+++ b/addons/account_voucher/test/case2_usd_eur_debtor_in_usd.yml
@@ -179,7 +179,7 @@
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_19'))])
- self.signal_proforma_voucher(cr, uid, voucher)
+ self.signal_workflow(cr, uid, voucher, 'proforma_voucher')
-
I check that the move of my voucher is valid
-
@@ -266,7 +266,7 @@
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_19'))])
- self.signal_proforma_voucher(cr, uid, voucher)
+ self.signal_workflow(cr, uid, voucher, 'proforma_voucher')
-
I check that my voucher state is posted
-
diff --git a/addons/account_voucher/test/case3_eur_eur.yml b/addons/account_voucher/test/case3_eur_eur.yml
index 99153ceeca4..064b79ed305 100644
--- a/addons/account_voucher/test/case3_eur_eur.yml
+++ b/addons/account_voucher/test/case3_eur_eur.yml
@@ -141,7 +141,7 @@
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 3'),('partner_id', '=', ref('base.res_partner_19'))])
- self.signal_proforma_voucher(cr, uid, voucher)
+ self.signal_workflow(cr, uid, voucher, 'proforma_voucher')
-
I check that the move of my first voucher is valid
-
@@ -228,7 +228,8 @@
-
!python {model: account.voucher}: |
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 3'), ('partner_id', '=', ref('base.res_partner_19'))])
- self.signal_proforma_voucher(cr, uid, voucher)
+ self.signal_workflow(cr, uid, voucher, 'proforma_voucher')
+
-
I check that the move of my second voucher is valid
-
diff --git a/addons/account_voucher/test/case4_cad_chf.yml b/addons/account_voucher/test/case4_cad_chf.yml
index 90c36e371bf..476a22c3f15 100644
--- a/addons/account_voucher/test/case4_cad_chf.yml
+++ b/addons/account_voucher/test/case4_cad_chf.yml
@@ -88,12 +88,13 @@
I check that first invoice move is correct for debtor account (debit - credit == 149.39)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_cad"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == 149.39), "Invoice move is incorrect for debtors account"
+ assert float_compare(move_line.debit - move_line.credit, 149.39, 2) == 0, "Invoice move is incorrect for debtors account"
-
I set the context that will be used for the encoding of all the vouchers of this file
-
@@ -142,9 +143,9 @@
I confirm the voucher
-
!python {model: account.voucher}: |
- from openerp import netsvc
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 4'), ('partner_id', '=', ref('base.res_partner_19'))])
- self.signal_proforma_voucher(cr, uid, voucher)
+ self.signal_workflow(cr, uid, voucher, 'proforma_voucher')
+
-
I check that the move of my voucher is valid
-
@@ -166,15 +167,17 @@
I check that my writeoff is correct. 11.05 credit and -13.26 amount_currency
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 4'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
+ assert move_lines, "Voucher move has no lines"
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.amount_currency == 200:
- assert move_line.debit == 160.00, "Bank account has wrong entry."
- elif move_line.amount_currency == -298.78:
- assert move_line.credit == 149.39, "Debtor account has wrong entry."
+ if float_compare(move_line.amount_currency, 200, 2) == 0:
+ assert float_compare(move_line.debit, 160.00, 2) == 0, "Bank account has wrong entry."
+ elif float_compare(move_line.amount_currency, -298.78, 2) == 0:
+ assert float_compare(move_line.credit, 149.39, 2) == 0, "Debtor account has wrong entry."
elif move_line.debit == 0.00 and move_line.credit == 0.00:
assert move_line.amount_currency == 98.78, "Incorrect Currency Difference, got %s as amount_currency (expected 98.78)." % (move_line.amount_currency)
assert move_line.currency_id.id == ref('base.CAD'), "Incorrect Currency Difference, got %s (expected 'CAD')" % (move_line.currency_id.name)
diff --git a/addons/account_voucher/test/case5_suppl_usd_usd.yml b/addons/account_voucher/test/case5_suppl_usd_usd.yml
index 917e2366f56..c20d07a8192 100644
--- a/addons/account_voucher/test/case5_suppl_usd_usd.yml
+++ b/addons/account_voucher/test/case5_suppl_usd_usd.yml
@@ -16,14 +16,14 @@
-
!record {model: res.currency.rate, id: nov_usd}:
currency_id: base.USD
- name: !eval "'%s-11-01' %(datetime.now().year)"
+ name: !eval "'%s-11-01 00:00:00' %(datetime.now().year)"
rate: 1.8
-
I create currency USD in OpenERP for December of 1.5 Rate
-
!record {model: res.currency.rate, id: dec_usd}:
currency_id: base.USD
- name: !eval "'%s-12-01' %(datetime.now().year)"
+ name: !eval "'%s-12-01 00:00:00' %(datetime.now().year)"
rate: 1.5
-
I set the income and expense currency accounts on the main company
@@ -92,13 +92,16 @@
I check that first invoice move is correct for creditor account(debit - credit == -555.56)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_supplier_invoice_november"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == -555.56), "Invoice move is incorrect for creditor account"
- assert (move_line.amount_currency == -1000), "Amount currency is incorrect for creditor account"
+ assert float_compare(move_line.debit - move_line.credit, -555.56, 2) == 0, \
+ "Invoice move is incorrect for creditor account"
+ assert float_compare(move_line.amount_currency, -1000, 2) == 0, \
+ "Amount currency is incorrect for creditor account"
-
I set the context that will be used for the encoding of all the vouchers of this file
-
diff --git a/addons/account_voucher/test/case_eur_usd.yml b/addons/account_voucher/test/case_eur_usd.yml
index 825fae1d1af..69304c342c9 100644
--- a/addons/account_voucher/test/case_eur_usd.yml
+++ b/addons/account_voucher/test/case_eur_usd.yml
@@ -107,6 +107,7 @@
for item in voucher.line_cr_ids:
if item.amount_unreconciled == 1400:
data += [(item.id, 1400)]
+ assert data, "Credit line not found"
for line_id, amount in data:
self.pool.get('account.voucher.line').write(cr, uid, [line_id], {'amount': amount})
assert (voucher.state=='draft'), "Voucher is not in draft state"
@@ -170,5 +171,6 @@
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for Invoice"
-
+ assert move_line.amount_residual_currency == 0.0, "Residual amount is not correct for Invoice"
+ assert move_line.amount_residual == 0.0, "Residual amount is not correct for Invoice"
+ assert invoice_id.state == 'paid', "Invoice is not paid"
diff --git a/addons/account_voucher/test/sales_payment.yml b/addons/account_voucher/test/sales_payment.yml
index ed9305a320c..7bd0e18a5c4 100644
--- a/addons/account_voucher/test/sales_payment.yml
+++ b/addons/account_voucher/test/sales_payment.yml
@@ -68,7 +68,7 @@
voucher_id = self.browse(cr, uid, id)
assert (voucher_id.writeoff_amount == 0.0), "Writeoff amount is not 0.0"
assert (voucher_id.state=='draft'), "Voucher is not in draft state"
- self.signal_proforma_voucher(cr, uid, [voucher_id.id])
+ voucher_id.signal_workflow('proforma_voucher')
-
Finally i will Confirm the state of the invoice is paid
diff --git a/addons/account_voucher/test/sales_receipt.yml b/addons/account_voucher/test/sales_receipt.yml
index e75eda2aac0..9bae2c3549b 100644
--- a/addons/account_voucher/test/sales_receipt.yml
+++ b/addons/account_voucher/test/sales_receipt.yml
@@ -69,7 +69,7 @@
id = self.create(cr, uid, vals)
voucher_id = self.browse(cr, uid, id)
assert (voucher_id.state=='draft'), "Voucher is not in draft state"
- self.signal_proforma_voucher(cr, uid, [voucher_id.id])
+ voucher_id.signal_workflow('proforma_voucher')
-
I check that move lines are reconciled meaning voucher is paid
-
diff --git a/addons/analytic/analytic.py b/addons/analytic/analytic.py
index 1d728ba1e7c..e31efa0eb2a 100644
--- a/addons/analytic/analytic.py
+++ b/addons/analytic/analytic.py
@@ -159,7 +159,8 @@ class account_analytic_account(osv.osv):
if account.company_id.currency_id.id != value:
raise osv.except_osv(_('Error!'), _("If you set a company, the currency selected has to be the same as it's currency. \nYou can remove the company belonging, and thus change the currency, only on analytic account of type 'view'. This can be really useful for consolidation purposes of several companies charts with different currencies, for example."))
if value:
- return cr.execute("""update account_analytic_account set currency_id=%s where id=%s""", (value, account.id, ))
+ cr.execute("""update account_analytic_account set currency_id=%s where id=%s""", (value, account.id))
+ self.invalidate_cache(cr, uid, ['currency_id'], [account.id], context=context)
def _currency(self, cr, uid, ids, field_name, arg, context=None):
result = {}
@@ -173,7 +174,7 @@ class account_analytic_account(osv.osv):
_columns = {
'name': fields.char('Account/Contract Name', required=True, track_visibility='onchange'),
'complete_name': fields.function(_get_full_name, type='char', string='Full Name'),
- 'code': fields.char('Reference', select=True, track_visibility='onchange'),
+ 'code': fields.char('Reference', select=True, track_visibility='onchange', copy=False),
'type': fields.selection([('view','Analytic View'), ('normal','Analytic Account'),('contract','Contract or Project'),('template','Template of Contract')], 'Type of Account', required=True,
help="If you select the View Type, it means you won\'t allow to create journal entries using that account.\n"\
"The type 'Analytic account' stands for usual accounts that you only want to use in accounting.\n"\
@@ -196,7 +197,14 @@ class account_analytic_account(osv.osv):
'date_start': fields.date('Start Date'),
'date': fields.date('Expiration Date', select=True, track_visibility='onchange'),
'company_id': fields.many2one('res.company', 'Company', required=False), #not required because we want to allow different companies to use the same chart of account, except for leaf accounts.
- 'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'),('pending','To Renew'),('close','Closed'),('cancelled', 'Cancelled')], 'Status', required=True, track_visibility='onchange'),
+ 'state': fields.selection([('template', 'Template'),
+ ('draft','New'),
+ ('open','In Progress'),
+ ('pending','To Renew'),
+ ('close','Closed'),
+ ('cancelled', 'Cancelled')],
+ 'Status', required=True,
+ track_visibility='onchange', copy=False),
'currency_id': fields.function(_currency, fnct_inv=_set_company_currency, #the currency_id field is readonly except if it's a view account and if there is no company
store = {
'res.company': (_get_analytic_account, ['currency_id'], 10),
@@ -266,10 +274,7 @@ class account_analytic_account(osv.osv):
if not default:
default = {}
analytic = self.browse(cr, uid, id, context=context)
- default.update(
- code=False,
- line_ids=[],
- name=_("%s (copy)") % (analytic['name']))
+ default['name'] = _("%s (copy)") % analytic['name']
return super(account_analytic_account, self).copy(cr, uid, id, default, context=context)
def on_change_company(self, cr, uid, id, company_id):
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 3682755b9c4..5c251708d91 100644
--- a/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py
+++ b/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py
@@ -19,7 +19,6 @@
#
##############################################################################
from openerp.osv import fields, osv
-from openerp.osv.orm import intersect
from openerp.tools.translate import _
from openerp.addons.decimal_precision import decimal_precision as dp
@@ -128,7 +127,7 @@ class account_analytic_account(osv.osv):
act_obj = self.pool.get('ir.actions.act_window')
dummy, act_window_id = mod_obj.get_object_reference(cr, uid, 'hr_expense', 'expense_all')
- result = act_obj.read(cr, uid, act_window_id, context=context)
+ result = act_obj.read(cr, uid, [act_window_id], context=context)[0]
line_ids = self.pool.get('hr.expense.line').search(cr,uid,[('analytic_account', 'in', ids)])
result['domain'] = [('line_ids', 'in', line_ids)]
diff --git a/addons/analytic_user_function/analytic_user_function.py b/addons/analytic_user_function/analytic_user_function.py
index 5a17ac91e53..42530bfbb7b 100644
--- a/addons/analytic_user_function/analytic_user_function.py
+++ b/addons/analytic_user_function/analytic_user_function.py
@@ -59,7 +59,7 @@ class analytic_user_funct_grid(osv.osv):
class account_analytic_account(osv.osv):
_inherit = "account.analytic.account"
_columns = {
- 'user_product_ids': fields.one2many('analytic.user.funct.grid', 'account_id', 'Users/Products Rel.'),
+ 'user_product_ids': fields.one2many('analytic.user.funct.grid', 'account_id', 'Users/Products Rel.', copy=True),
}
diff --git a/addons/auth_crypt/auth_crypt.py b/addons/auth_crypt/auth_crypt.py
index 6c9deb51e92..384e7960cbe 100644
--- a/addons/auth_crypt/auth_crypt.py
+++ b/addons/auth_crypt/auth_crypt.py
@@ -32,6 +32,7 @@ class res_users(osv.osv):
def set_pw(self, cr, uid, id, name, value, args, context):
if value:
self._set_password(cr, uid, id, value, context=context)
+ self.invalidate_cache(cr, uid, context=context)
def get_pw( self, cr, uid, ids, name, args, context ):
cr.execute('select id, password from res_users where id in %s', (tuple(map(int, ids)),))
@@ -39,7 +40,7 @@ class res_users(osv.osv):
_columns = {
'password': fields.function(get_pw, fnct_inv=set_pw, type='char', string='Password', invisible=True, store=True),
- 'password_crypt': fields.char(string='Encrypted Password', invisible=True),
+ 'password_crypt': fields.char(string='Encrypted Password', invisible=True, copy=False),
}
def check_credentials(self, cr, uid, password):
@@ -50,6 +51,7 @@ class res_users(osv.osv):
stored, encrypted = cr.fetchone()
if stored and not encrypted:
self._set_password(cr, uid, uid, stored)
+ self.invalidate_cache(cr, uid)
try:
return super(res_users, self).check_credentials(cr, uid, password)
except openerp.exceptions.AccessDenied:
diff --git a/addons/auth_crypt/i18n/base_crypt.pot b/addons/auth_crypt/i18n/base_crypt.pot
deleted file mode 100644
index 4aa6bbcdc54..00000000000
--- a/addons/auth_crypt/i18n/base_crypt.pot
+++ /dev/null
@@ -1,22 +0,0 @@
-# Translation of OpenERP Server.
-# This file contains the translation of the following modules:
-# * base_crypt
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: OpenERP Server 7.0alpha\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2012-12-03 16:03+0000\n"
-"Last-Translator: <>\n"
-"Language-Team: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: \n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
diff --git a/addons/auth_crypt/i18n/bg.po b/addons/auth_crypt/i18n/bg.po
deleted file mode 100644
index 24e74238284..00000000000
--- a/addons/auth_crypt/i18n/bg.po
+++ /dev/null
@@ -1,76 +0,0 @@
-# Bulgarian translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2011-02-18 09:47+0000\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: Bulgarian \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
-#~ msgid ""
-#~ "This module replaces the cleartext password in the database with a password "
-#~ "hash,\n"
-#~ "preventing anyone from reading the original password.\n"
-#~ "For your existing user base, the removal of the cleartext passwords occurs "
-#~ "the first time\n"
-#~ "a user logs into the database, after installing base_crypt.\n"
-#~ "After installing this module it won't be possible to recover a forgotten "
-#~ "password for your\n"
-#~ "users, the only solution is for an admin to set a new password.\n"
-#~ "\n"
-#~ "Note: installing this module does not mean you can ignore basic security "
-#~ "measures,\n"
-#~ "as the password is still transmitted unencrypted on the network (by the "
-#~ "client),\n"
-#~ "unless you are using a secure protocol such as XML-RPCS.\n"
-#~ " "
-#~ msgstr ""
-#~ "Този модул заменя паролите в чист текст в базата данни с хеширани такива,\n"
-#~ " за предотвратяване прочита на оригиналната парола.\n"
-#~ " За съществуващата потребителска база, когато премахването на паролите в "
-#~ "чист текст се случва за първи път,\n"
-#~ " влизане на потребител става, след инсталиране на base_crypt.\n"
-#~ " След като инсталирате този модул няма да бъде възможно да се възстанови "
-#~ "забравена парола за\n"
-#~ " потребители, единственото решение е администратор, да зададе нова парола.\n"
-#~ "\n"
-#~ " Забележка: инсталиране на този модул не значи, че може да пренебрегне "
-#~ "основните мерки за сигурност,\n"
-#~ " като парола все още е изпратена в прав текст в мрежата (от клиента),\n"
-#~ " освен ако не използвате защитен протокол, като XML-RPCS.\n"
-#~ " "
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "Грешка"
-
-#~ msgid "Base - Password Encryption"
-#~ msgstr "База - Криптиране на пароли"
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "Моля изберете парола!"
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr "Избраната фирма не е измежду разрешените фирми за този потребител"
-
-#~ msgid "res.users"
-#~ msgstr "res.users"
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "Не може да има двама потребители с един и същ \"логин\"!"
diff --git a/addons/auth_crypt/i18n/ca.po b/addons/auth_crypt/i18n/ca.po
deleted file mode 100644
index 2d08f1ab474..00000000000
--- a/addons/auth_crypt/i18n/ca.po
+++ /dev/null
@@ -1,78 +0,0 @@
-# Catalan translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2011-03-26 18:08+0000\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: Catalan \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
-#~ msgid ""
-#~ "This module replaces the cleartext password in the database with a password "
-#~ "hash,\n"
-#~ "preventing anyone from reading the original password.\n"
-#~ "For your existing user base, the removal of the cleartext passwords occurs "
-#~ "the first time\n"
-#~ "a user logs into the database, after installing base_crypt.\n"
-#~ "After installing this module it won't be possible to recover a forgotten "
-#~ "password for your\n"
-#~ "users, the only solution is for an admin to set a new password.\n"
-#~ "\n"
-#~ "Note: installing this module does not mean you can ignore basic security "
-#~ "measures,\n"
-#~ "as the password is still transmitted unencrypted on the network (by the "
-#~ "client),\n"
-#~ "unless you are using a secure protocol such as XML-RPCS.\n"
-#~ " "
-#~ msgstr ""
-#~ "Aquest mòdul substitueix la contrasenya en text pla per un hash codificat,\n"
-#~ "prevenint que algú pugui llegir la contrasenya original.\n"
-#~ "Per a un usuari existent, l'esborrat de la contrasenya en text pla es "
-#~ "realitza la primera\n"
-#~ "vegada que l'usuari es connecta després d'instal·lar base_crypt.\n"
-#~ "Després d'instal·lar aquest mòdul, els usuaris no podran recuperar la seva "
-#~ "contrasenya,\n"
-#~ "un administrador haurà d'introduir una nova contrasenya.\n"
-#~ "\n"
-#~ "Nota: Instal·lar aquest mòdul no significa que podeu ignorar les mesures "
-#~ "bàsiques de seguretat,\n"
-#~ "perquè la contrasenya és enviada sense codificar pel client,\n"
-#~ "a menys que utilitzeu un protocol segur com XML-RPCS.\n"
-#~ " "
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "Si us plau, escriviu una contrasenya!"
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr ""
-#~ "La companyia seleccionada no està en les companyies permeses per aquest "
-#~ "usuari"
-
-#~ msgid "res.users"
-#~ msgstr "res.usuaris"
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "Error"
-
-#~ msgid "Base - Password Encryption"
-#~ msgstr "Base - Encriptació de la Contrasenya"
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "No podeu tenir dos usuaris amb el mateix identificador d'usuari!"
diff --git a/addons/auth_crypt/i18n/el.po b/addons/auth_crypt/i18n/el.po
deleted file mode 100644
index 4adc016c3e1..00000000000
--- a/addons/auth_crypt/i18n/el.po
+++ /dev/null
@@ -1,24 +0,0 @@
-# Translation of OpenERP Server.
-# This file contains the translation of the following modules:
-# * base_crypt
-#
-# Copyright (C) 2008,2009 P. Christeas
-# <> <>, 2009.
-msgid ""
-msgstr ""
-"Project-Id-Version: OpenERP Server 5.0.0\n"
-"Report-Msgid-Bugs-To: support@openerp.com\n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2011-02-15 15:37+0000\n"
-"Last-Translator: <> <>\n"
-"Language-Team: <>\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
diff --git a/addons/auth_crypt/i18n/es_CL.po b/addons/auth_crypt/i18n/es_CL.po
deleted file mode 100644
index 3f53b8b8b88..00000000000
--- a/addons/auth_crypt/i18n/es_CL.po
+++ /dev/null
@@ -1,80 +0,0 @@
-# Spanish (Chile) translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2011-10-03 17:17+0000\n"
-"Last-Translator: David Acevedo Toledo \n"
-"Language-Team: Spanish (Chile) \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "Error!"
-
-#~ msgid "res.users"
-#~ msgstr "res.users"
-
-#~ msgid "Base - Password Encryption"
-#~ msgstr "Base - Encriptación de la Contraseña"
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "¡Por favor, escriba una contraseña!"
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr ""
-#~ "La compañía seleccionada no está dentro de las compañías autorizadas para "
-#~ "este usuario"
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "¡No puede tener dos usuarios con el mismo nombre!"
-
-#~ msgid ""
-#~ "This module replaces the cleartext password in the database with a password "
-#~ "hash,\n"
-#~ "preventing anyone from reading the original password.\n"
-#~ "For your existing user base, the removal of the cleartext passwords occurs "
-#~ "the first time\n"
-#~ "a user logs into the database, after installing base_crypt.\n"
-#~ "After installing this module it won't be possible to recover a forgotten "
-#~ "password for your\n"
-#~ "users, the only solution is for an admin to set a new password.\n"
-#~ "\n"
-#~ "Note: installing this module does not mean you can ignore basic security "
-#~ "measures,\n"
-#~ "as the password is still transmitted unencrypted on the network (by the "
-#~ "client),\n"
-#~ "unless you are using a secure protocol such as XML-RPCS.\n"
-#~ " "
-#~ msgstr ""
-#~ "Este módulo sustituye la contraseña escrita en texto plano por una "
-#~ "codificada,\n"
-#~ "previniendo que alguien pueda leer la contraseña original.\n"
-#~ "Para un usuario existente, el sustitución de la contraseña en texto plano se "
-#~ "realiza la primera vez\n"
-#~ "que el usuario se conecte después de instalar base_crypt.\n"
-#~ "Después de instalar este módulo los usuarios no podrán recuperar su "
-#~ "contraseña olvidada,\n"
-#~ "un administrador tendrá que cambiarla por una nueva.\n"
-#~ "\n"
-#~ "Nota: instalar este módulo no significa que pueda ignorar las medidas "
-#~ "básicas de seguridad,\n"
-#~ "como la contraseña que es enviada por el cliente que sigue sin ser "
-#~ "codificada en la red,\n"
-#~ "a menos que utilice un protocolo seguro como XML-RPCS.\n"
-#~ " "
diff --git a/addons/auth_crypt/i18n/es_CR.po b/addons/auth_crypt/i18n/es_CR.po
deleted file mode 100644
index 35f8372f4c7..00000000000
--- a/addons/auth_crypt/i18n/es_CR.po
+++ /dev/null
@@ -1,79 +0,0 @@
-# Spanish translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2012-02-13 19:04+0000\n"
-"Last-Translator: Carlos Vásquez (CLEARCORP) "
-"\n"
-"Language-Team: Spanish \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-"Language: es\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
-#~ msgid "res.users"
-#~ msgstr "res.usuarios"
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "¡No puede tener dos usuarios con el mismo identificador de usuario!"
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr ""
-#~ "La compañía seleccionada no está autorizada como compañía para este usuario"
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "¡Por favor, escriba una contraseña!"
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "Error"
-
-#~ msgid ""
-#~ "This module replaces the cleartext password in the database with a password "
-#~ "hash,\n"
-#~ "preventing anyone from reading the original password.\n"
-#~ "For your existing user base, the removal of the cleartext passwords occurs "
-#~ "the first time\n"
-#~ "a user logs into the database, after installing base_crypt.\n"
-#~ "After installing this module it won't be possible to recover a forgotten "
-#~ "password for your\n"
-#~ "users, the only solution is for an admin to set a new password.\n"
-#~ "\n"
-#~ "Note: installing this module does not mean you can ignore basic security "
-#~ "measures,\n"
-#~ "as the password is still transmitted unencrypted on the network (by the "
-#~ "client),\n"
-#~ "unless you are using a secure protocol such as XML-RPCS.\n"
-#~ " "
-#~ msgstr ""
-#~ "Este módulo sustituye la contraseña en texto plano por un hash codificado,\n"
-#~ "previniendo que alguien pueda leer la contraseña original.\n"
-#~ "Para un usuario existente, el borrado de la contraseña en texto plano se "
-#~ "realiza la primera vez\n"
-#~ "que el usuario se conecte después de instalar base_crypt.\n"
-#~ "Después de instalar este módulo los usuarios no podrán recuperar su "
-#~ "contraseña,\n"
-#~ "un administrador tendrá que introducir una nueva contraseña.\n"
-#~ "\n"
-#~ "Nota: instalar este módulo no significa que pueda ignorar las medidas "
-#~ "básicas de seguridad,\n"
-#~ "porque la contraseña es enviada sin codificar por el cliente,\n"
-#~ "a menos que utilice un protocolo seguro como XML-RPCS.\n"
-#~ " "
-
-#~ msgid "Base - Password Encryption"
-#~ msgstr "Base - Encriptación de la Contraseña"
diff --git a/addons/auth_crypt/i18n/es_MX.po b/addons/auth_crypt/i18n/es_MX.po
deleted file mode 100644
index be7c1499288..00000000000
--- a/addons/auth_crypt/i18n/es_MX.po
+++ /dev/null
@@ -1,86 +0,0 @@
-# Spanish translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2011-01-11 11:14+0000\n"
-"PO-Revision-Date: 2011-02-15 15:37+0000\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: Spanish \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2011-09-05 05:58+0000\n"
-"X-Generator: Launchpad (build 13830)\n"
-
-#. module: base_crypt
-#: sql_constraint:res.users:0
-msgid "You can not have two users with the same login !"
-msgstr "¡No puede tener dos usuarios con el mismo identificador de usuario!"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "res.users"
-msgstr "res.usuarios"
-
-#. module: base_crypt
-#: constraint:res.users:0
-msgid "The chosen company is not in the allowed companies for this user"
-msgstr ""
-"La compañía seleccionada no está autorizada como compañía para este usuario"
-
-#. module: base_crypt
-#: code:addons/base_crypt/crypt.py:132
-#, python-format
-msgid "Please specify the password !"
-msgstr "¡Por favor, escriba una contraseña!"
-
-#. module: base_crypt
-#: model:ir.module.module,shortdesc:base_crypt.module_meta_information
-msgid "Base - Password Encryption"
-msgstr "Base - Encriptación de la Contraseña"
-
-#. module: base_crypt
-#: code:addons/base_crypt/crypt.py:132
-#, python-format
-msgid "Error"
-msgstr "Error"
-
-#. module: base_crypt
-#: model:ir.module.module,description:base_crypt.module_meta_information
-msgid ""
-"This module replaces the cleartext password in the database with a password "
-"hash,\n"
-"preventing anyone from reading the original password.\n"
-"For your existing user base, the removal of the cleartext passwords occurs "
-"the first time\n"
-"a user logs into the database, after installing base_crypt.\n"
-"After installing this module it won't be possible to recover a forgotten "
-"password for your\n"
-"users, the only solution is for an admin to set a new password.\n"
-"\n"
-"Note: installing this module does not mean you can ignore basic security "
-"measures,\n"
-"as the password is still transmitted unencrypted on the network (by the "
-"client),\n"
-"unless you are using a secure protocol such as XML-RPCS.\n"
-" "
-msgstr ""
-"Este módulo sustituye la contraseña en texto plano por un hash codificado,\n"
-"previniendo que alguien pueda leer la contraseña original.\n"
-"Para un usuario existente, el borrado de la contraseña en texto plano se "
-"realiza la primera vez\n"
-"que el usuario se conecte después de instalar base_crypt.\n"
-"Después de instalar este módulo los usuarios no podrán recuperar su "
-"contraseña,\n"
-"un administrador tendrá que introducir una nueva contraseña.\n"
-"\n"
-"Nota: instalar este módulo no significa que pueda ignorar las medidas "
-"básicas de seguridad,\n"
-"porque la contraseña es enviada sin codificar por el cliente,\n"
-"a menos que utilice un protocolo seguro como XML-RPCS.\n"
-" "
diff --git a/addons/auth_crypt/i18n/es_PY.po b/addons/auth_crypt/i18n/es_PY.po
deleted file mode 100644
index 8e5cbcbee86..00000000000
--- a/addons/auth_crypt/i18n/es_PY.po
+++ /dev/null
@@ -1,78 +0,0 @@
-# Spanish (Paraguay) translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2011-03-08 17:36+0000\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: Spanish (Paraguay) \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
-#~ msgid ""
-#~ "This module replaces the cleartext password in the database with a password "
-#~ "hash,\n"
-#~ "preventing anyone from reading the original password.\n"
-#~ "For your existing user base, the removal of the cleartext passwords occurs "
-#~ "the first time\n"
-#~ "a user logs into the database, after installing base_crypt.\n"
-#~ "After installing this module it won't be possible to recover a forgotten "
-#~ "password for your\n"
-#~ "users, the only solution is for an admin to set a new password.\n"
-#~ "\n"
-#~ "Note: installing this module does not mean you can ignore basic security "
-#~ "measures,\n"
-#~ "as the password is still transmitted unencrypted on the network (by the "
-#~ "client),\n"
-#~ "unless you are using a secure protocol such as XML-RPCS.\n"
-#~ " "
-#~ msgstr ""
-#~ "Este módulo sustituye la contraseña en texto plano por un hash codificado,\n"
-#~ "previniendo que alguien pueda leer la contraseña original.\n"
-#~ "Para un usuario existente, el borrado de la contraseña en texto plano se "
-#~ "realiza la primera vez\n"
-#~ "que el usuario se conecte después de instalar base_crypt.\n"
-#~ "Después de instalar este módulo los usuarios no podrán recuperar su "
-#~ "contraseña,\n"
-#~ "un administrador tendrá que introducir una nueva contraseña.\n"
-#~ "\n"
-#~ "Nota: instalar este módulo no significa que pueda ignorar las medidas "
-#~ "básicas de seguridad,\n"
-#~ "porque la contraseña es enviada sin codificar por el cliente,\n"
-#~ "a menos que utilice un protocolo seguro como XML-RPCS.\n"
-#~ " "
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "Error!"
-
-#~ msgid "Base - Password Encryption"
-#~ msgstr "Base - Encriptación de la Contraseña"
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "¡Por favor, escriba una contraseña!"
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr ""
-#~ "La compañía seleccionada no está en las compañías permitidas para este "
-#~ "usuario"
-
-#~ msgid "res.users"
-#~ msgstr "res.users"
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "¡No puede tener dos usuarios con el mismo identificador de usuario!"
diff --git a/addons/auth_crypt/i18n/es_VE.po b/addons/auth_crypt/i18n/es_VE.po
deleted file mode 100644
index be7c1499288..00000000000
--- a/addons/auth_crypt/i18n/es_VE.po
+++ /dev/null
@@ -1,86 +0,0 @@
-# Spanish translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2011-01-11 11:14+0000\n"
-"PO-Revision-Date: 2011-02-15 15:37+0000\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: Spanish \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2011-09-05 05:58+0000\n"
-"X-Generator: Launchpad (build 13830)\n"
-
-#. module: base_crypt
-#: sql_constraint:res.users:0
-msgid "You can not have two users with the same login !"
-msgstr "¡No puede tener dos usuarios con el mismo identificador de usuario!"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "res.users"
-msgstr "res.usuarios"
-
-#. module: base_crypt
-#: constraint:res.users:0
-msgid "The chosen company is not in the allowed companies for this user"
-msgstr ""
-"La compañía seleccionada no está autorizada como compañía para este usuario"
-
-#. module: base_crypt
-#: code:addons/base_crypt/crypt.py:132
-#, python-format
-msgid "Please specify the password !"
-msgstr "¡Por favor, escriba una contraseña!"
-
-#. module: base_crypt
-#: model:ir.module.module,shortdesc:base_crypt.module_meta_information
-msgid "Base - Password Encryption"
-msgstr "Base - Encriptación de la Contraseña"
-
-#. module: base_crypt
-#: code:addons/base_crypt/crypt.py:132
-#, python-format
-msgid "Error"
-msgstr "Error"
-
-#. module: base_crypt
-#: model:ir.module.module,description:base_crypt.module_meta_information
-msgid ""
-"This module replaces the cleartext password in the database with a password "
-"hash,\n"
-"preventing anyone from reading the original password.\n"
-"For your existing user base, the removal of the cleartext passwords occurs "
-"the first time\n"
-"a user logs into the database, after installing base_crypt.\n"
-"After installing this module it won't be possible to recover a forgotten "
-"password for your\n"
-"users, the only solution is for an admin to set a new password.\n"
-"\n"
-"Note: installing this module does not mean you can ignore basic security "
-"measures,\n"
-"as the password is still transmitted unencrypted on the network (by the "
-"client),\n"
-"unless you are using a secure protocol such as XML-RPCS.\n"
-" "
-msgstr ""
-"Este módulo sustituye la contraseña en texto plano por un hash codificado,\n"
-"previniendo que alguien pueda leer la contraseña original.\n"
-"Para un usuario existente, el borrado de la contraseña en texto plano se "
-"realiza la primera vez\n"
-"que el usuario se conecte después de instalar base_crypt.\n"
-"Después de instalar este módulo los usuarios no podrán recuperar su "
-"contraseña,\n"
-"un administrador tendrá que introducir una nueva contraseña.\n"
-"\n"
-"Nota: instalar este módulo no significa que pueda ignorar las medidas "
-"básicas de seguridad,\n"
-"porque la contraseña es enviada sin codificar por el cliente,\n"
-"a menos que utilice un protocolo seguro como XML-RPCS.\n"
-" "
diff --git a/addons/auth_crypt/i18n/fa.po b/addons/auth_crypt/i18n/fa.po
deleted file mode 100644
index e4291629d6f..00000000000
--- a/addons/auth_crypt/i18n/fa.po
+++ /dev/null
@@ -1,23 +0,0 @@
-# Persian translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2011-12-18 20:12+0000\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: Persian \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
diff --git a/addons/auth_crypt/i18n/fi.po b/addons/auth_crypt/i18n/fi.po
deleted file mode 100644
index 39221ce5d69..00000000000
--- a/addons/auth_crypt/i18n/fi.po
+++ /dev/null
@@ -1,40 +0,0 @@
-# Finnish translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2011-06-08 10:57+0000\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: Finnish \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "Virhe"
-
-#~ msgid "Base - Password Encryption"
-#~ msgstr "Base - Salasanan kryptaus"
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "Määrittele salasana !"
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr "Valittu yritys ei ole sallittu tälle käyttäjälle"
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "Kahdella eri käyttäjällä ei voi olla samaa käyttäjätunnusta!"
diff --git a/addons/auth_crypt/i18n/gu.po b/addons/auth_crypt/i18n/gu.po
deleted file mode 100644
index 6d506cb3573..00000000000
--- a/addons/auth_crypt/i18n/gu.po
+++ /dev/null
@@ -1,37 +0,0 @@
-# Gujarati 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-12-03 16:03+0000\n"
-"PO-Revision-Date: 2012-04-19 08:51+0000\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: Gujarati \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "તમે બે વપરાશકર્તાઓને એક જ લોગીન ન કરી શકો!"
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "પાસવર્ડ સ્પષ્ટ કરો!"
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr "પસંદ કરેલ કંપની માન્ય કંપનીઓમાં આ વપરાશકર્તા માટે નથી"
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "ભૂલ"
diff --git a/addons/auth_crypt/i18n/id.po b/addons/auth_crypt/i18n/id.po
deleted file mode 100644
index 035480b7a84..00000000000
--- a/addons/auth_crypt/i18n/id.po
+++ /dev/null
@@ -1,23 +0,0 @@
-# Indonesian 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-12-03 16:03+0000\n"
-"PO-Revision-Date: 2012-12-09 08:46+0000\n"
-"Last-Translator: Budhi Hartono \n"
-"Language-Team: Indonesian \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-10 04:39+0000\n"
-"X-Generator: Launchpad (build 16341)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr "Pengguna"
diff --git a/addons/auth_crypt/i18n/ja.po b/addons/auth_crypt/i18n/ja.po
deleted file mode 100644
index d92df144a2c..00000000000
--- a/addons/auth_crypt/i18n/ja.po
+++ /dev/null
@@ -1,40 +0,0 @@
-# Japanese 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-12-03 16:03+0000\n"
-"PO-Revision-Date: 2012-04-01 06:05+0000\n"
-"Last-Translator: Masaki Yamaya \n"
-"Language-Team: Japanese \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "同一のログインに2つのユーザを持つことはできません!"
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "エラー"
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr "選択した会社は、このユーザに許された会社ではありません。"
-
-#~ msgid "res.users"
-#~ msgstr "res.users"
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "パスワードを指定してください!"
diff --git a/addons/auth_crypt/i18n/lv.po b/addons/auth_crypt/i18n/lv.po
deleted file mode 100644
index 4d34fec70a9..00000000000
--- a/addons/auth_crypt/i18n/lv.po
+++ /dev/null
@@ -1,40 +0,0 @@
-# Latvian 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-12-03 16:03+0000\n"
-"PO-Revision-Date: 2012-10-16 16:11+0000\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: Latvian \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "Nevar būt divi lietotāji ar vienādu pieteikuma vārdu!"
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "Kļūda"
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr "Izvēlētais uzņēmums nav šim lietotājam atļauto uzņēmumu sarakstā"
-
-#~ msgid "res.users"
-#~ msgstr "res.users"
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "Lūdzu norādiet paroli!"
diff --git a/addons/auth_crypt/i18n/nb.po b/addons/auth_crypt/i18n/nb.po
deleted file mode 100644
index c9e2d8709d1..00000000000
--- a/addons/auth_crypt/i18n/nb.po
+++ /dev/null
@@ -1,44 +0,0 @@
-# Norwegian Bokmal translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2012-12-04 20:38+0000\n"
-"Last-Translator: Kaare Pettersen \n"
-"Language-Team: Norwegian Bokmal \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-05 05:20+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr "Brukere."
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "Feil"
-
-#~ msgid "Base - Password Encryption"
-#~ msgstr "Basis - Passord kryptering"
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "Vennligst angi passordet !"
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr ""
-#~ "Det valgte firmaet er ikke i listen over tillatte firmaer for denne brukeren"
-
-#~ msgid "res.users"
-#~ msgstr "res.users"
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "Du kan ikke ha to brukere med samme login !"
diff --git a/addons/auth_crypt/i18n/oc.po b/addons/auth_crypt/i18n/oc.po
deleted file mode 100644
index a6cd3aa45a1..00000000000
--- a/addons/auth_crypt/i18n/oc.po
+++ /dev/null
@@ -1,45 +0,0 @@
-# Occitan (post 1500) translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2011-11-20 09:17+0000\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: Occitan (post 1500) \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "Error"
-
-#~ msgid "Base - Password Encryption"
-#~ msgstr "Basa - Chifratge del senhal"
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "Entratz un senhal"
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr ""
-#~ "La societat causida fa pas partida de las societats autorizadas per aqueste "
-#~ "utilizaire"
-
-#~ msgid "res.users"
-#~ msgstr "res.users"
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "Podètz pas aver dos utilizaires amb lo meteis identificant !"
diff --git a/addons/auth_crypt/i18n/sk.po b/addons/auth_crypt/i18n/sk.po
deleted file mode 100644
index b082277e875..00000000000
--- a/addons/auth_crypt/i18n/sk.po
+++ /dev/null
@@ -1,41 +0,0 @@
-# Slovak translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2011-02-21 08:14+0000\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: Slovak \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "Chyba"
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "Prosím, zadajte heslo!"
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr ""
-#~ "Vybraná spoločnosť nie je medzi schválenými spoločnosťami tohto používateľa"
-
-#~ msgid "res.users"
-#~ msgstr "res.users"
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "Nemôžte mať dvoch používateľov s rovnakým pristúpovým menom!"
diff --git a/addons/auth_crypt/i18n/sq.po b/addons/auth_crypt/i18n/sq.po
deleted file mode 100644
index c57af2d287b..00000000000
--- a/addons/auth_crypt/i18n/sq.po
+++ /dev/null
@@ -1,23 +0,0 @@
-# Albanian translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2011-03-28 15:26+0000\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: Albanian \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
diff --git a/addons/auth_crypt/i18n/sr@latin.po b/addons/auth_crypt/i18n/sr@latin.po
deleted file mode 100644
index 9e9547f154b..00000000000
--- a/addons/auth_crypt/i18n/sr@latin.po
+++ /dev/null
@@ -1,76 +0,0 @@
-# Serbian Latin translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2011-10-05 13:25+0000\n"
-"Last-Translator: Milan Milosevic \n"
-"Language-Team: Serbian Latin \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "Greška"
-
-#~ msgid "Base - Password Encryption"
-#~ msgstr "Baza - Šifrovanje lozinke"
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "Molimo odredite lozinku"
-
-#~ msgid "res.users"
-#~ msgstr "res.users"
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "Ne možete imati dva korisnika sa istom prijavom!"
-
-#~ msgid ""
-#~ "This module replaces the cleartext password in the database with a password "
-#~ "hash,\n"
-#~ "preventing anyone from reading the original password.\n"
-#~ "For your existing user base, the removal of the cleartext passwords occurs "
-#~ "the first time\n"
-#~ "a user logs into the database, after installing base_crypt.\n"
-#~ "After installing this module it won't be possible to recover a forgotten "
-#~ "password for your\n"
-#~ "users, the only solution is for an admin to set a new password.\n"
-#~ "\n"
-#~ "Note: installing this module does not mean you can ignore basic security "
-#~ "measures,\n"
-#~ "as the password is still transmitted unencrypted on the network (by the "
-#~ "client),\n"
-#~ "unless you are using a secure protocol such as XML-RPCS.\n"
-#~ " "
-#~ msgstr ""
-#~ "Ovaj modul zamenjuje vidljivu lozinku u bazi podataka znacima,\n"
-#~ "što onemogućava bilo koga da pročita originalnu lozinku.\n"
-#~ "Što se tiče Vaše baze podataka, ukidanje vidljive lozinke dešava se prvi put "
-#~ "pošto se korisnik prijavi u bazu podataka, po instalaciji base_crypt.\n"
-#~ "Po instalaciji ovog modula neće biti moguće izmeniti zaboravljenu lozinku za "
-#~ "Vaše korisnike, jedino rešenje će biti da im administrator postavi novu "
-#~ "lozinku.\n"
-#~ "\n"
-#~ "Beleška: Instaliranje ovog modula ne znači da možete ignorisati osnovne "
-#~ "sigurnosne mere,\n"
-#~ "budući da se lozinka i dalje prenosi nešifrovana u mreži (od strane "
-#~ "klijenta),\n"
-#~ "ukoliko ne koristite siguran protokol kao XML-RPCS.\n"
-#~ " "
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr "Odabrano preduzeće nije u dozvoljenim preduzećima za ovog korisnioka"
diff --git a/addons/auth_crypt/i18n/vi.po b/addons/auth_crypt/i18n/vi.po
deleted file mode 100644
index 4ad1a22fdaf..00000000000
--- a/addons/auth_crypt/i18n/vi.po
+++ /dev/null
@@ -1,80 +0,0 @@
-# Vietnamese translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
-# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openobject-addons\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2012-12-03 16:03+0000\n"
-"PO-Revision-Date: 2011-07-20 09:55+0000\n"
-"Last-Translator: OpenBMS JSC \n"
-"Language-Team: Vietnamese \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n"
-"X-Generator: Launchpad (build 16335)\n"
-
-#. module: base_crypt
-#: model:ir.model,name:base_crypt.model_res_users
-msgid "Users"
-msgstr ""
-
-#, python-format
-#~ msgid "Error"
-#~ msgstr "Lỗi"
-
-#~ msgid "Base - Password Encryption"
-#~ msgstr "Cơ sở - Mã hóa Mật khẩu"
-
-#, python-format
-#~ msgid "Please specify the password !"
-#~ msgstr "Vui lòng xác định mật khẩu !"
-
-#~ msgid "The chosen company is not in the allowed companies for this user"
-#~ msgstr ""
-#~ "Công ty được lựa chọn không nằm trong các công ty mà người sử dụng này được "
-#~ "phép"
-
-#~ msgid "res.users"
-#~ msgstr "res.users"
-
-#~ msgid "You can not have two users with the same login !"
-#~ msgstr "Bạn không thể có hai người sử dụng với cùng tên đăng nhập !"
-
-#~ msgid ""
-#~ "This module replaces the cleartext password in the database with a password "
-#~ "hash,\n"
-#~ "preventing anyone from reading the original password.\n"
-#~ "For your existing user base, the removal of the cleartext passwords occurs "
-#~ "the first time\n"
-#~ "a user logs into the database, after installing base_crypt.\n"
-#~ "After installing this module it won't be possible to recover a forgotten "
-#~ "password for your\n"
-#~ "users, the only solution is for an admin to set a new password.\n"
-#~ "\n"
-#~ "Note: installing this module does not mean you can ignore basic security "
-#~ "measures,\n"
-#~ "as the password is still transmitted unencrypted on the network (by the "
-#~ "client),\n"
-#~ "unless you are using a secure protocol such as XML-RPCS.\n"
-#~ " "
-#~ msgstr ""
-#~ "Mô-đun này thay thế mật khẩu dạng tường minh (cleartext) trong cơ sở dữ liệu "
-#~ "với một mật khẩu băm (hash),\n"
-#~ "ngăn chặn bất cứ ai đọc các mật khẩu ban đầu.\n"
-#~ "Đối với người dùng hiện tại của bạn, việc loại bỏ các mật khẩu tường minh "
-#~ "xảy ra lần đầu tiên\n"
-#~ "người dùng đăng nhập vào cơ sở dữ liệu, sau khi cài đặt base_crypt.\n"
-#~ "Sau khi cài đặt mô-đun này, sẽ không thể khôi phục lại một mật khẩu bị lãng "
-#~ "quên cho\n"
-#~ "người sử dụng của bạn, giải pháp duy nhất là để một quản trị viên thiết lập "
-#~ "một mật khẩu mới.\n"
-#~ "\n"
-#~ "Lưu ý: cài đặt mô-đun này không có nghĩa là bạn có thể bỏ qua các biện pháp "
-#~ "bảo mật cơ bản,\n"
-#~ "như mật khẩu vẫn được truyền không mã hóa trên mạng (từ máy khách),\n"
-#~ "trừ khi bạn sử dụng một giao thức an toàn chẳng hạn như XML-RPCS.\n"
-#~ " "
diff --git a/addons/auth_ldap/users_ldap.py b/addons/auth_ldap/users_ldap.py
index e6a7f0f4716..9ddc3d6ed45 100644
--- a/addons/auth_ldap/users_ldap.py
+++ b/addons/auth_ldap/users_ldap.py
@@ -237,14 +237,14 @@ class res_company(osv.osv):
_inherit = "res.company"
_columns = {
'ldaps': fields.one2many(
- 'res.company.ldap', 'company', 'LDAP Parameters'),
+ 'res.company.ldap', 'company', 'LDAP Parameters', copy=True),
}
class users(osv.osv):
_inherit = "res.users"
- def login(self, db, login, password):
- user_id = super(users, self).login(db, login, password)
+ def _login(self, db, login, password):
+ user_id = super(users, self)._login(db, login, password)
if user_id:
return user_id
registry = RegistryManager.get(db)
diff --git a/addons/auth_oauth/res_users.py b/addons/auth_oauth/res_users.py
index 8d57a23f702..d2aed236f75 100644
--- a/addons/auth_oauth/res_users.py
+++ b/addons/auth_oauth/res_users.py
@@ -17,8 +17,8 @@ class res_users(osv.Model):
_columns = {
'oauth_provider_id': fields.many2one('auth.oauth.provider', 'OAuth Provider'),
- 'oauth_uid': fields.char('OAuth User ID', help="Oauth Provider user_id"),
- 'oauth_access_token': fields.char('OAuth Access Token', readonly=True),
+ 'oauth_uid': fields.char('OAuth User ID', help="Oauth Provider user_id", copy=False),
+ 'oauth_access_token': fields.char('OAuth Access Token', readonly=True, copy=False),
}
_sql_constraints = [
diff --git a/addons/auth_openid/res_users.py b/addons/auth_openid/res_users.py
index 897dea07982..1c6c635118a 100644
--- a/addons/auth_openid/res_users.py
+++ b/addons/auth_openid/res_users.py
@@ -30,11 +30,11 @@ class res_users(osv.osv):
# TODO create helper fields for autofill openid_url and openid_email -> http://pad.openerp.com/web-openid
_columns = {
- 'openid_url': fields.char('OpenID URL', size=1024),
- 'openid_email': fields.char('OpenID Email', size=256,
+ 'openid_url': fields.char('OpenID URL', size=1024, copy=False),
+ 'openid_email': fields.char('OpenID Email', size=256, copy=False,
help="Used for disambiguation in case of a shared OpenID URL"),
'openid_key': fields.char('OpenID Key', size=utils.KEY_LENGTH,
- readonly=True),
+ readonly=True, copy=False),
}
def _check_openid_url_email(self, cr, uid, ids, context=None):
@@ -48,19 +48,8 @@ class res_users(osv.osv):
(_check_openid_url_email, lambda self, *a, **kw: self._check_openid_url_email_msg(*a, **kw), ['active', 'openid_url', 'openid_email']),
]
- def copy(self, cr, uid, rid, defaults=None, context=None):
- reset_fields = 'openid_url openid_email'.split()
- reset_values = dict.fromkeys(reset_fields, False)
- if defaults is None:
- defaults = reset_values
- else:
- defaults = dict(reset_values, **defaults)
-
- defaults['openid_key'] = False
- return super(res_users, self).copy(cr, uid, rid, defaults, context)
-
- def login(self, db, login, password):
- result = super(res_users, self).login(db, login, password)
+ def _login(self, db, login, password):
+ result = super(res_users, self)._login(db, login, password)
if result:
return result
else:
@@ -69,6 +58,7 @@ class res_users(osv.osv):
SET login_date=now() AT TIME ZONE 'UTC'
WHERE login=%s AND openid_key=%s AND active=%s RETURNING id""",
(tools.ustr(login), tools.ustr(password), True))
+ # beware: record cache may be invalid
res = cr.fetchone()
cr.commit()
return res[0] if res else False
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py
index 576a778be45..97bcea52bc1 100644
--- a/addons/auth_signup/res_users.py
+++ b/addons/auth_signup/res_users.py
@@ -104,9 +104,9 @@ class res_partner(osv.Model):
return self._get_signup_url_for_action(cr, uid, ids, context=context)
_columns = {
- 'signup_token': fields.char('Signup Token'),
- 'signup_type': fields.char('Signup Token Type'),
- 'signup_expiration': fields.datetime('Signup Expiration'),
+ 'signup_token': fields.char('Signup Token', copy=False),
+ 'signup_type': fields.char('Signup Token Type', copy=False),
+ 'signup_expiration': fields.datetime('Signup Expiration', copy=False),
'signup_valid': fields.function(_get_signup_valid, type='boolean', string='Signup Token is Valid'),
'signup_url': fields.function(_get_signup_url, type='char', string='Signup URL'),
}
@@ -302,7 +302,7 @@ class res_users(osv.Model):
user_id = super(res_users, self).create(cr, uid, values, context=context)
user = self.browse(cr, uid, user_id, context=context)
if user.email and not context.get('no_reset_password'):
- context.update({'create_user': True})
+ context = dict(context, create_user=True)
try:
self.action_reset_password(cr, uid, [user.id], context=context)
except MailDeliveryException:
diff --git a/addons/auth_signup/signup.xml b/addons/auth_signup/signup.xml
deleted file mode 100644
index d01ee58f2f1..00000000000
--- a/addons/auth_signup/signup.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
- signup.signup.form
- signup.signup
- form
-
-
-
-
-
-
- signup.signup
- ir.actions.act_window
- signup.signup
- form
- form
- new
-
-
-
-
diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py
index 1f5fcc58ccf..b743603f805 100644
--- a/addons/base_action_rule/base_action_rule.py
+++ b/addons/base_action_rule/base_action_rule.py
@@ -57,7 +57,7 @@ class base_action_rule(osv.osv):
'name': fields.char('Rule Name', required=True),
'model_id': fields.many2one('ir.model', 'Related Document Model',
required=True, domain=[('osv_memory', '=', False)]),
- 'model': fields.related('model_id', 'model', type="char", size=256, string='Model'),
+ 'model': fields.related('model_id', 'model', type="char", string='Model'),
'create_date': fields.datetime('Create Date', readonly=1),
'active': fields.boolean('Active',
help="When unchecked, the rule is hidden and will not be executed."),
@@ -96,7 +96,7 @@ class base_action_rule(osv.osv):
ondelete='restrict',
domain="[('model_id', '=', model_id.model)]",
help="If present, this condition must be satisfied before executing the action rule."),
- 'last_run': fields.datetime('Last Run', readonly=1),
+ 'last_run': fields.datetime('Last Run', readonly=1, copy=False),
}
_defaults = {
@@ -152,64 +152,6 @@ class base_action_rule(osv.osv):
return True
- def _wrap_create(self, old_create, model):
- """ Return a wrapper around `old_create` calling both `old_create` and
- `_process`, in that order.
- """
- def create(cr, uid, vals, context=None, **kwargs):
- # avoid loops or cascading actions
- if context and context.get('action'):
- return old_create(cr, uid, vals, context=context)
-
- context = dict(context or {}, action=True)
- new_id = old_create(cr, uid, vals, context=context, **kwargs)
-
- # retrieve the action rules to run on creation
- action_dom = [('model', '=', model), ('kind', 'in', ['on_create', 'on_create_or_write'])]
- action_ids = self.search(cr, uid, action_dom, context=context)
-
- # check postconditions, and execute actions on the records that satisfy them
- for action in self.browse(cr, uid, action_ids, context=context):
- if self._filter(cr, uid, action, action.filter_id, [new_id], context=context):
- self._process(cr, uid, action, [new_id], context=context)
- return new_id
-
- return create
-
- def _wrap_write(self, old_write, model):
- """ Return a wrapper around `old_write` calling both `old_write` and
- `_process`, in that order.
- """
- def write(cr, uid, ids, vals, context=None, **kwargs):
- # avoid loops or cascading actions
- if context and context.get('action'):
- return old_write(cr, uid, ids, vals, context=context, **kwargs)
-
- context = dict(context or {}, action=True)
- ids = [ids] if isinstance(ids, (int, long, str)) else ids
-
- # retrieve the action rules to run on update
- action_dom = [('model', '=', model), ('kind', 'in', ['on_write', 'on_create_or_write'])]
- action_ids = self.search(cr, uid, action_dom, context=context)
- actions = self.browse(cr, uid, action_ids, context=context)
-
- # check preconditions
- pre_ids = {}
- for action in actions:
- pre_ids[action] = self._filter(cr, uid, action, action.filter_pre_id, ids, context=context)
-
- # execute write
- old_write(cr, uid, ids, vals, context=context, **kwargs)
-
- # check postconditions, and execute actions on the records that satisfy them
- for action in actions:
- post_ids = self._filter(cr, uid, action, action.filter_id, pre_ids[action], context=context)
- if post_ids:
- self._process(cr, uid, action, post_ids, context=context)
- return True
-
- return write
-
def _register_hook(self, cr, ids=None):
""" Wrap the methods `create` and `write` of the models specified by
the rules given by `ids` (or all existing rules if `ids` is `None`.)
@@ -221,10 +163,65 @@ class base_action_rule(osv.osv):
model = action_rule.model_id.model
model_obj = self.pool[model]
if not hasattr(model_obj, 'base_action_ruled'):
- model_obj.create = self._wrap_create(model_obj.create, model)
- model_obj.write = self._wrap_write(model_obj.write, model)
+ # monkey-patch methods create and write
+
+ def create(self, cr, uid, vals, context=None, **kwargs):
+ # avoid loops or cascading actions
+ if context and context.get('action'):
+ return create.origin(self, cr, uid, vals, context=context)
+
+ # call original method with a modified context
+ context = dict(context or {}, action=True)
+ new_id = create.origin(self, cr, uid, vals, context=context, **kwargs)
+
+ # as it is a new record, we do not consider the actions that have a prefilter
+ action_model = self.pool.get('base.action.rule')
+ action_dom = [('model', '=', self._name),
+ ('kind', 'in', ['on_create', 'on_create_or_write'])]
+ action_ids = action_model.search(cr, uid, action_dom, context=context)
+
+ # check postconditions, and execute actions on the records that satisfy them
+ for action in action_model.browse(cr, uid, action_ids, context=context):
+ if action_model._filter(cr, uid, action, action.filter_id, [new_id], context=context):
+ action_model._process(cr, uid, action, [new_id], context=context)
+ return new_id
+
+ def write(self, cr, uid, ids, vals, context=None, **kwargs):
+ # avoid loops or cascading actions
+ if context and context.get('action'):
+ return write.origin(self, cr, uid, ids, vals, context=context)
+
+ # modify context
+ context = dict(context or {}, action=True)
+ ids = [ids] if isinstance(ids, (int, long, str)) else ids
+
+ # retrieve the action rules to possibly execute
+ action_model = self.pool.get('base.action.rule')
+ action_dom = [('model', '=', self._name),
+ ('kind', 'in', ['on_write', 'on_create_or_write'])]
+ action_ids = action_model.search(cr, uid, action_dom, context=context)
+ actions = action_model.browse(cr, uid, action_ids, context=context)
+
+ # check preconditions
+ pre_ids = {}
+ for action in actions:
+ pre_ids[action] = action_model._filter(cr, uid, action, action.filter_pre_id, ids, context=context)
+
+ # call original method
+ write.origin(self, cr, uid, ids, vals, context=context, **kwargs)
+
+ # check postconditions, and execute actions on the records that satisfy them
+ for action in actions:
+ post_ids = action_model._filter(cr, uid, action, action.filter_id, pre_ids[action], context=context)
+ if post_ids:
+ action_model._process(cr, uid, action, post_ids, context=context)
+ return True
+
+ model_obj._patch_method('create', create)
+ model_obj._patch_method('write', write)
model_obj.base_action_ruled = True
updated = True
+
return updated
def create(self, cr, uid, vals, context=None):
@@ -283,7 +280,7 @@ class base_action_rule(osv.osv):
if 'lang' not in ctx:
# Filters might be language-sensitive, attempt to reuse creator lang
# as we are usually running this as super-user in background
- [filter_meta] = action.filter_id.perm_read()
+ [filter_meta] = action.filter_id.get_metadata()
user_id = filter_meta['write_uid'] and filter_meta['write_uid'][0] or \
filter_meta['create_uid'][0]
ctx['lang'] = self.pool['res.users'].browse(cr, uid, user_id).lang
diff --git a/addons/base_gengo/res_company.py b/addons/base_gengo/res_company.py
index 1b083ecf5a2..8400db8f943 100644
--- a/addons/base_gengo/res_company.py
+++ b/addons/base_gengo/res_company.py
@@ -26,8 +26,8 @@ class res_company(osv.Model):
_name = "res.company"
_inherit = "res.company"
_columns = {
- "gengo_private_key": fields.text("Gengo Private Key"),
- "gengo_public_key": fields.text("Gengo Public Key"),
+ "gengo_private_key": fields.text("Gengo Private Key", copy=False),
+ "gengo_public_key": fields.text("Gengo Public Key", copy=False),
"gengo_comment": fields.text("Comments", help="This comment will be automatically be enclosed in each an every request sent to Gengo"),
"gengo_auto_approve": fields.boolean("Auto Approve Translation ?", help="Jobs are Automatically Approved by Gengo."),
"gengo_sandbox": fields.boolean("Sandbox Mode", help="Check this box if you're using the sandbox mode of Gengo, mainly used for testing purpose."),
diff --git a/addons/base_import/models.py b/addons/base_import/models.py
index 2b683bd8927..bc4bb0a268f 100644
--- a/addons/base_import/models.py
+++ b/addons/base_import/models.py
@@ -74,6 +74,7 @@ class ir_import(orm.TransientModel):
:param str model: name of the model to get fields form
:param int landing: depth of recursion into o2m fields
"""
+ model_obj = self.pool[model]
fields = [{
'id': 'id',
'name': 'id',
@@ -81,8 +82,11 @@ class ir_import(orm.TransientModel):
'required': False,
'fields': [],
}]
- fields_got = self.pool[model].fields_get(cr, uid, context=context)
+ fields_got = model_obj.fields_get(cr, uid, context=context)
+ blacklist = orm.MAGIC_COLUMNS + [model_obj.CONCURRENCY_CHECK_FIELD]
for name, field in fields_got.iteritems():
+ if name in blacklist:
+ continue
# an empty string means the field is deprecated, @deprecated must
# be absent or False to mean not-deprecated
if field.get('deprecated', False) is not False:
diff --git a/addons/base_import/tests/test_cases.py b/addons/base_import/tests/test_cases.py
index 5479ae94428..ac5a4440523 100644
--- a/addons/base_import/tests/test_cases.py
+++ b/addons/base_import/tests/test_cases.py
@@ -4,48 +4,72 @@ from openerp.tests.common import TransactionCase
from .. import models
-ID_FIELD = {'id': 'id', 'name': 'id', 'string': "External ID", 'required': False, 'fields': []}
+ID_FIELD = {
+ 'id': 'id',
+ 'name': 'id',
+ 'string': "External ID",
+ 'required': False,
+ 'fields': [],
+}
+DISPLAY_NAME_FIELD = {
+ 'id': 'display_name',
+ 'name': 'display_name',
+ 'string': "Name",
+ 'required': False,
+ 'fields': [],
+}
+
def make_field(name='value', string='unknown', required=False, fields=[]):
return [
ID_FIELD,
+ DISPLAY_NAME_FIELD,
{'id': name, 'name': name, 'string': string, 'required': required, 'fields': fields},
]
-class test_basic_fields(TransactionCase):
+def sorted_fields(fields):
+ """ recursively sort field lists to ease comparison """
+ recursed = [dict(field, fields=sorted_fields(field['fields'])) for field in fields]
+ return sorted(recursed, key=lambda field: field['id'])
+
+class BaseImportCase(TransactionCase):
+ def assertEqualFields(self, fields1, fields2):
+ self.assertEqual(sorted_fields(fields1), sorted_fields(fields2))
+
+class test_basic_fields(BaseImportCase):
def get_fields(self, field):
return self.registry('base_import.import')\
.get_fields(self.cr, self.uid, 'base_import.tests.models.' + field)
def test_base(self):
""" A basic field is not required """
- self.assertEqual(self.get_fields('char'), make_field())
+ self.assertEqualFields(self.get_fields('char'), make_field())
def test_required(self):
""" Required fields should be flagged (so they can be fill-required) """
- self.assertEqual(self.get_fields('char.required'), make_field(required=True))
+ self.assertEqualFields(self.get_fields('char.required'), make_field(required=True))
def test_readonly(self):
""" Readonly fields should be filtered out"""
- self.assertEqual(self.get_fields('char.readonly'), [ID_FIELD])
+ self.assertEqualFields(self.get_fields('char.readonly'), [ID_FIELD, DISPLAY_NAME_FIELD])
def test_readonly_states(self):
""" Readonly fields with states should not be filtered out"""
- self.assertEqual(self.get_fields('char.states'), make_field())
+ self.assertEqualFields(self.get_fields('char.states'), make_field())
def test_readonly_states_noreadonly(self):
""" Readonly fields with states having nothing to do with
readonly should still be filtered out"""
- self.assertEqual(self.get_fields('char.noreadonly'), [ID_FIELD])
+ self.assertEqualFields(self.get_fields('char.noreadonly'), [ID_FIELD, DISPLAY_NAME_FIELD])
def test_readonly_states_stillreadonly(self):
""" Readonly fields with readonly states leaving them readonly
always... filtered out"""
- self.assertEqual(self.get_fields('char.stillreadonly'), [ID_FIELD])
+ self.assertEqualFields(self.get_fields('char.stillreadonly'), [ID_FIELD, DISPLAY_NAME_FIELD])
def test_m2o(self):
""" M2O fields should allow import of themselves (name_get),
their id and their xid"""
- self.assertEqual(self.get_fields('m2o'), make_field(fields=[
+ self.assertEqualFields(self.get_fields('m2o'), make_field(fields=[
{'id': 'value', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []},
{'id': 'value', 'name': '.id', 'string': 'Database ID', 'required': False, 'fields': []},
]))
@@ -55,19 +79,20 @@ class test_basic_fields(TransactionCase):
required as well (the client has to handle that: requiredness
is id-based)
"""
- self.assertEqual(self.get_fields('m2o.required'), make_field(required=True, fields=[
+ self.assertEqualFields(self.get_fields('m2o.required'), make_field(required=True, fields=[
{'id': 'value', 'name': 'id', 'string': 'External ID', 'required': True, 'fields': []},
{'id': 'value', 'name': '.id', 'string': 'Database ID', 'required': True, 'fields': []},
]))
-class test_o2m(TransactionCase):
+class test_o2m(BaseImportCase):
def get_fields(self, field):
return self.registry('base_import.import')\
.get_fields(self.cr, self.uid, 'base_import.tests.models.' + field)
def test_shallow(self):
- self.assertEqual(self.get_fields('o2m'), make_field(fields=[
- {'id': 'id', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []},
+ self.assertEqualFields(self.get_fields('o2m'), make_field(fields=[
+ ID_FIELD,
+ DISPLAY_NAME_FIELD,
# FIXME: should reverse field be ignored?
{'id': 'parent_id', 'name': 'parent_id', 'string': 'unknown', 'required': False, 'fields': [
{'id': 'parent_id', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []},
@@ -224,7 +249,8 @@ class test_preview(TransactionCase):
self.assertEqual(result['headers'], ['name', 'Some Value', 'Counter'])
# Order depends on iteration order of fields_get
self.assertItemsEqual(result['fields'], [
- {'id': 'id', 'name': 'id', 'string': 'External ID', 'required':False, 'fields': []},
+ ID_FIELD,
+ DISPLAY_NAME_FIELD,
{'id': 'name', 'name': 'name', 'string': 'Name', 'required':False, 'fields': []},
{'id': 'somevalue', 'name': 'somevalue', 'string': 'Some Value', 'required':True, 'fields': []},
{'id': 'othervalue', 'name': 'othervalue', 'string': 'Other Variable', 'required':False, 'fields': []},
diff --git a/addons/base_report_designer/base_report_designer.py b/addons/base_report_designer/base_report_designer.py
index bc7959a17df..a7289f42d4e 100644
--- a/addons/base_report_designer/base_report_designer.py
+++ b/addons/base_report_designer/base_report_designer.py
@@ -59,10 +59,8 @@ class report_xml(osv.osv):
return True
def report_get(self, cr, uid, report_id, context=None):
- if context is None:
- context = {}
# skip osv.fields.sanitize_binary_value() because we want the raw bytes in all cases
- context.update(bin_raw=True)
+ context = dict(context or {}, bin_raw=True)
report = self.browse(cr, uid, report_id, context=context)
sxw_data = report.report_sxw_content
rml_data = report.report_rml_content
diff --git a/addons/base_report_designer/wizard/base_report_designer_modify.py b/addons/base_report_designer/wizard/base_report_designer_modify.py
index e8ed45e7ce1..a28c562ad14 100644
--- a/addons/base_report_designer/wizard/base_report_designer_modify.py
+++ b/addons/base_report_designer/wizard/base_report_designer_modify.py
@@ -133,16 +133,19 @@ class base_report_rml_save(osv.osv_memory):
"""
res = super(base_report_rml_save, self).default_get(cr, uid, fields, context=context)
- report_id = self.pool['base.report.sxw'].search(cr,uid,[])
- data = self.pool['base.report.file.sxw'].read(cr, uid, report_id, context=context)[0]
+ report_ids = self.pool['base.report.sxw'].search(cr,uid,[], context=context)
+
+ data = self.pool['base.report.file.sxw'].read(cr, uid, report_ids, context=context)[0]
+
report = self.pool['ir.actions.report.xml'].browse(cr, uid, data['report_id'], context=context)
if 'file_rml' in fields:
res['file_rml'] = base64.encodestring(report.report_rml_content)
return res
+
_columns = {
- 'file_rml':fields.binary('Save As'),
- }
+ 'file_rml':fields.binary('Save As'),
+ }
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/board/controllers.py b/addons/board/controllers.py
index 93634627352..6ab5d7035c0 100644
--- a/addons/board/controllers.py
+++ b/addons/board/controllers.py
@@ -2,7 +2,7 @@
from xml.etree import ElementTree
from openerp.addons.web.controllers.main import load_actions_from_ir_values
-from openerp.addons.web.http import Controller, route, request
+from openerp.http import Controller, route, request
class Board(Controller):
@route('/board/add_to_dashboard', type='json', auth='user')
diff --git a/addons/calendar/base_calendar_view.xml b/addons/calendar/base_calendar_view.xml
deleted file mode 100644
index 7fa20a848fd..00000000000
--- a/addons/calendar/base_calendar_view.xml
+++ /dev/null
@@ -1,291 +0,0 @@
-
-
-
-
-
-
-
-
-
- res.alarm.form
- res.alarm
-
-
-
-
-
-
-
- res.alarm.tree
- res.alarm
-
-
-
-
-
-
-
-
-
-
-
- Alarms
- ir.actions.act_window
- res.alarm
- form
- tree,form
-
-
- Click to setup a new alarm type.
-
- You can define a customized type of calendar alarm that may be
- assigned to calendar events or meetings.
-
-
-
-
-
-
-
-
-
- Event Form
- calendar.event
-
-
-
-
-
-
-
- Event Tree
- calendar.event
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Events Calendar
- calendar.event
-
-
-
-
-
-
-
-
-
-
-
-
- Calendar Events Search
- calendar.event
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Events
- ir.actions.act_window
- calendar.event
- form
- calendar,tree,form
-
-
-
-
-
-
-
-
diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py
index 7108a2d1d73..a77eb5ea6f6 100644
--- a/addons/calendar/calendar.py
+++ b/addons/calendar/calendar.py
@@ -297,7 +297,7 @@ class res_partner(osv.Model):
Used by web_calendar.js : Many2ManyAttendee
"""
datas = []
- meeting = False
+ meeting = None
if meeting_id:
meeting = self.pool['calendar.event'].browse(cr, uid, get_real_ids(meeting_id), context=context)
for partner in self.browse(cr, uid, ids, context=context):
@@ -309,7 +309,7 @@ class res_partner(osv.Model):
datas.append(data)
return datas
- def calendar_last_notif_ack(self, cr, uid, context=None):
+ def _set_calendar_last_notif_ack(self, cr, uid, context=None):
partner = self.pool['res.users'].browse(cr, uid, uid, context=context).partner_id
self.write(cr, uid, partner.id, {'calendar_last_notif_ack': datetime.now()}, context=context)
return
@@ -708,6 +708,7 @@ class calendar_event(osv.Model):
return (format_date, format_time)
def get_display_time_tz(self, cr, uid, ids, tz=False, context=None):
+ context = dict(context or {})
if tz:
context["tz"] = tz
ev = self.browse(cr, uid, ids, context=context)[0]
@@ -720,8 +721,7 @@ class calendar_event(osv.Model):
1) if user add duration for 2 hours, return : August-23-2013 at (04-30 To 06-30) (Europe/Brussels)
2) if event all day ,return : AllDay, July-31-2013
"""
- if context is None:
- context = {}
+ context = dict(context or {})
tz = context.get('tz', False)
if not tz: # tz can have a value False, so dont do it in the default value of get !
@@ -782,7 +782,7 @@ class calendar_event(osv.Model):
if data.count and data.count <= 0:
raise osv.except_osv(_('Warning!'), _('Count cannot be negative or 0.'))
- data = self.read(cr, uid, id, ['id', 'byday', 'recurrency', 'month_list', 'final_date', 'rrule_type', 'month_by', 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'day', 'week_list'], context=context)
+ data = self.read(cr, uid, id, ['id', 'byday', 'recurrency', 'final_date', 'rrule_type', 'month_by', 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'day', 'week_list'], context=context)
event = data['id']
if data['recurrency']:
result[event] = self.compute_rule_string(data)
@@ -900,7 +900,7 @@ class calendar_event(osv.Model):
'categ_ids': fields.many2many('calendar.event.type', 'meeting_category_rel', 'event_id', 'type_id', 'Tags'),
'attendee_ids': fields.one2many('calendar.attendee', 'event_id', 'Attendees', ondelete='cascade'),
'partner_ids': fields.many2many('res.partner', 'calendar_event_res_partner_rel', string='Attendees', states={'done': [('readonly', True)]}),
- 'alarm_ids': fields.many2many('calendar.alarm', 'calendar_alarm_calendar_event_rel', string='Reminders', ondelete="restrict"),
+ 'alarm_ids': fields.many2many('calendar.alarm', 'calendar_alarm_calendar_event_rel', string='Reminders', ondelete="restrict", copy=False),
}
_defaults = {
'end_type': 'count',
@@ -1404,16 +1404,9 @@ class calendar_event(osv.Model):
return res
def copy(self, cr, uid, id, default=None, context=None):
- if context is None:
- context = {}
-
default = default or {}
-
self._set_date(cr, uid, default, id=default.get('id'), context=context)
- default['attendee_ids'] = False
-
- res = super(calendar_event, self).copy(cr, uid, calendar_id2real_id(id), default, context)
- return res
+ return super(calendar_event, self).copy(cr, uid, calendar_id2real_id(id), default, context)
def _detach_one_event(self, cr, uid, id, values=dict(), context=None):
real_event_id = calendar_id2real_id(id)
@@ -1547,8 +1540,7 @@ class calendar_event(osv.Model):
return res
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True):
- if not context:
- context = {}
+ context = dict(context or {})
if 'date' in groupby:
raise osv.except_osv(_('Warning!'), _('Group by date is not supported, use the calendar view instead.'))
diff --git a/addons/calendar/controllers/main.py b/addons/calendar/controllers/main.py
index 7480c85d629..d0fdb47ab0b 100644
--- a/addons/calendar/controllers/main.py
+++ b/addons/calendar/controllers/main.py
@@ -1,7 +1,7 @@
import simplejson
import openerp
-import openerp.addons.web.http as http
-from openerp.addons.web.http import request
+import openerp.http as http
+from openerp.http import request
import openerp.addons.web.controllers.main as webmain
import json
@@ -67,5 +67,5 @@ class meeting_invitation(http.Controller):
uid = request.session.uid
context = request.session.context
with registry.cursor() as cr:
- res = registry.get("res.partner").calendar_last_notif_ack(cr, uid, context=context)
+ res = registry.get("res.partner")._set_calendar_last_notif_ack(cr, uid, context=context)
return res
diff --git a/addons/calendar/crm_meeting.py b/addons/calendar/crm_meeting.py
deleted file mode 100644
index 66823e0b8f4..00000000000
--- a/addons/calendar/crm_meeting.py
+++ /dev/null
@@ -1,176 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-today OpenERP SA ()
-#
-# 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 time
-
-from openerp.osv import fields, osv
-from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
-from openerp.tools.translate import _
-from base_calendar import get_real_ids, base_calendar_id2real_id
-#
-# crm.meeting is defined here so that it may be used by modules other than crm,
-# without forcing the installation of crm.
-#
-
-class crm_meeting_type(osv.Model):
- _name = 'crm.meeting.type'
- _description = 'Meeting Type'
- _columns = {
- 'name': fields.char('Name', size=64, required=True, translate=True),
- }
-
-class crm_meeting(osv.Model):
- """ Model for CRM meetings """
- _name = 'crm.meeting'
- _description = "Meeting"
- _order = "id desc"
- _inherit = ["calendar.event", "mail.thread", "ir.needaction_mixin"]
- _columns = {
- 'create_date': fields.datetime('Creation Date', readonly=True),
- 'write_date': fields.datetime('Write Date', readonly=True),
- 'date_open': fields.datetime('Confirmed', readonly=True),
- 'date_closed': fields.datetime('Closed', readonly=True),
- 'partner_ids': fields.many2many('res.partner', 'crm_meeting_partner_rel', 'meeting_id', 'partner_id',
- string='Attendees', states={'done': [('readonly', True)]}),
- 'state': fields.selection(
- [('draft', 'Unconfirmed'), ('open', 'Confirmed')],
- string='Status', size=16, readonly=True, track_visibility='onchange'),
- # Meeting fields
- 'name': fields.char('Meeting Subject', size=128, required=True, states={'done': [('readonly', True)]}),
- 'categ_ids': fields.many2many('crm.meeting.type', 'meeting_category_rel',
- 'event_id', 'type_id', 'Tags'),
- 'attendee_ids': fields.many2many('calendar.attendee', 'meeting_attendee_rel',\
- 'event_id', 'attendee_id', 'Invited People', states={'done': [('readonly', True)]}),
- }
- _defaults = {
- 'state': 'open',
- }
-
- def message_get_subscription_data(self, cr, uid, ids, context=None):
- res = {}
- for virtual_id in ids:
- real_id = base_calendar_id2real_id(virtual_id)
- result = super(crm_meeting, self).message_get_subscription_data(cr, uid, [real_id], context=context)
- res[virtual_id] = result[real_id]
- return res
-
- def copy(self, cr, uid, id, default=None, context=None):
- default = default or {}
- default['attendee_ids'] = False
- return super(crm_meeting, self).copy(cr, uid, id, default, context)
-
- def write(self, cr, uid, ids, values, context=None):
- """ Override to add case management: open/close dates """
- if values.get('state')and values.get('state') == 'open':
- values['date_open'] = fields.datetime.now()
- return super(crm_meeting, self).write(cr, uid, ids, values, context=context)
-
- def onchange_partner_ids(self, cr, uid, ids, value, context=None):
- """ The basic purpose of this method is to check that destination partners
- effectively have email addresses. Otherwise a warning is thrown.
- :param value: value format: [[6, 0, [3, 4]]]
- """
- res = {'value': {}}
- if not value or not value[0] or not value[0][0] == 6:
- return
- res.update(self.check_partners_email(cr, uid, value[0][2], context=context))
- return res
-
- def check_partners_email(self, cr, uid, partner_ids, context=None):
- """ Verify that selected partner_ids have an email_address defined.
- Otherwise throw a warning. """
- partner_wo_email_lst = []
- for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids, context=context):
- if not partner.email:
- partner_wo_email_lst.append(partner)
- if not partner_wo_email_lst:
- return {}
- warning_msg = _('The following contacts have no email address :')
- for partner in partner_wo_email_lst:
- warning_msg += '\n- %s' % (partner.name)
- return {'warning': {
- 'title': _('Email addresses not found'),
- 'message': warning_msg,
- }
- }
- # ----------------------------------------
- # OpenChatter
- # ----------------------------------------
-
- # shows events of the day for this user
- def _needaction_domain_get(self, cr, uid, context=None):
- return [('date', '<=', time.strftime(DEFAULT_SERVER_DATE_FORMAT + ' 23:59:59')), ('date_deadline', '>=', time.strftime(DEFAULT_SERVER_DATE_FORMAT + ' 23:59:59')), ('user_id', '=', uid)]
-
- def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification',
- subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
- if isinstance(thread_id, str):
- thread_id = get_real_ids(thread_id)
- return super(crm_meeting, self).message_post(cr, uid, thread_id, body=body, subject=subject, type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, **kwargs)
-
-class mail_message(osv.osv):
- _inherit = "mail.message"
-
- def search(self, cr, uid, args, offset=0, limit=0, order=None, context=None, count=False):
- '''
- convert the search on real ids in the case it was asked on virtual ids, then call super()
- '''
- for index in range(len(args)):
- if args[index][0] == "res_id" and isinstance(args[index][2], str):
- args[index][2] = get_real_ids(args[index][2])
- return super(mail_message, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count)
-
- def _find_allowed_model_wise(self, cr, uid, doc_model, doc_dict, context=None):
- if doc_model == 'crm.meeting':
- for virtual_id in self.pool[doc_model].get_recurrent_ids(cr, uid, doc_dict.keys(), [], context=context):
- doc_dict.setdefault(virtual_id, doc_dict[get_real_ids(virtual_id)])
- return super(mail_message, self)._find_allowed_model_wise(cr, uid, doc_model, doc_dict, context=context)
-
-class ir_attachment(osv.osv):
- _inherit = "ir.attachment"
-
- def search(self, cr, uid, args, offset=0, limit=0, order=None, context=None, count=False):
- '''
- convert the search on real ids in the case it was asked on virtual ids, then call super()
- '''
- for index in range(len(args)):
- if args[index][0] == "res_id" and isinstance(args[index][2], str):
- args[index][2] = get_real_ids(args[index][2])
- return super(ir_attachment, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count)
-
- def write(self, cr, uid, ids, vals, context=None):
- '''
- when posting an attachment (new or not), convert the virtual ids in real ids.
- '''
- if isinstance(vals.get('res_id'), str):
- vals['res_id'] = get_real_ids(vals.get('res_id'))
- return super(ir_attachment, self).write(cr, uid, ids, vals, context=context)
-
-class invite_wizard(osv.osv_memory):
- _inherit = 'mail.wizard.invite'
-
- def default_get(self, cr, uid, fields, context=None):
- '''
- in case someone clicked on 'invite others' wizard in the followers widget, transform virtual ids in real ids
- '''
- result = super(invite_wizard, self).default_get(cr, uid, fields, context=context)
- if 'res_id' in result:
- result['res_id'] = get_real_ids(result['res_id'])
- return result
diff --git a/addons/calendar/crm_meeting_data.xml b/addons/calendar/crm_meeting_data.xml
deleted file mode 100644
index d1cffddc403..00000000000
--- a/addons/calendar/crm_meeting_data.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
- Customer Meeting
-
-
-
- Internal Meeting
-
-
-
- Off-site Meeting
-
-
-
- Open Discussion
-
-
-
- Feedback Meeting
-
-
-
- Meeting
- crm.meeting
-
-
-
diff --git a/addons/crm/base_partner_merge.py b/addons/crm/base_partner_merge.py
index c07a32579f0..03311704613 100644
--- a/addons/crm/base_partner_merge.py
+++ b/addons/crm/base_partner_merge.py
@@ -281,7 +281,7 @@ class MergePartnerAutomatic(osv.TransientModel):
except (osv.except_osv, orm.except_orm):
_logger.info('Skip recursive partner hierarchies for parent_id %s of partner: %s', parent_id, dst_partner.id)
- @mute_logger('openerp.osv.expression', 'openerp.osv.orm')
+ @mute_logger('openerp.osv.expression', 'openerp.models')
def _merge(self, cr, uid, partner_ids, dst_partner=None, context=None):
proxy = self.pool.get('res.partner')
@@ -327,8 +327,7 @@ class MergePartnerAutomatic(osv.TransientModel):
information of the previous one and will copy the new cleaned email into
the email field.
"""
- if context is None:
- context = {}
+ context = dict(context or {})
proxy_model = self.pool['ir.model.fields']
field_ids = proxy_model.search(cr, uid, [('model', '=', 'res.partner'),
diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py
index 8a3bd5f2fbc..7d53c1d51eb 100644
--- a/addons/crm/crm_lead.py
+++ b/addons/crm/crm_lead.py
@@ -28,7 +28,6 @@ from openerp import SUPERUSER_ID
from openerp import tools
from openerp.addons.base.res.res_partner import format_address
from openerp.osv import fields, osv, orm
-from openerp.tools import html2plaintext
from openerp.tools.translate import _
CRM_LEAD_FIELDS_TO_MERGE = ['name',
@@ -82,6 +81,7 @@ class crm_lead(format_address, osv.osv):
_mail_mass_mailing = _('Leads / Opportunities')
def get_empty_list_help(self, cr, uid, help, context=None):
+ context = dict(context or {})
if context.get('default_type') == 'lead':
context['empty_list_help_model'] = 'crm.case.section'
context['empty_list_help_id'] = context.get('default_section_id')
@@ -224,7 +224,7 @@ class crm_lead(format_address, osv.osv):
"Filter 'Available for Mass Mailing' allows users to filter the leads when performing mass mailing."),
'type': fields.selection([ ('lead','Lead'), ('opportunity','Opportunity'), ],'Type', select=True, help="Type is used to separate Leads and Opportunities"),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True),
- 'date_closed': fields.datetime('Closed', readonly=True),
+ 'date_closed': fields.datetime('Closed', readonly=True, copy=False),
'stage_id': fields.many2one('crm.case.stage', 'Stage', track_visibility='onchange', select=True,
domain="['&', ('section_ids', '=', section_id), '|', ('type', '=', type), ('type', '=', 'both')]"),
'user_id': fields.many2one('res.users', 'Salesperson', select=True, track_visibility='onchange'),
@@ -883,8 +883,7 @@ class crm_lead(format_address, osv.osv):
return res
def create(self, cr, uid, vals, context=None):
- if context is None:
- context = {}
+ context = dict(context or {})
if vals.get('type') and not context.get('default_type'):
context['default_type'] = vals.get('type')
if vals.get('section_id') and not context.get('default_section_id'):
@@ -917,11 +916,10 @@ class crm_lead(format_address, osv.osv):
default['date_open'] = fields.datetime.now()
else:
default['date_open'] = False
- default['date_closed'] = False
- default['stage_id'] = self._get_default_stage_id(cr, uid, local_context)
- return super(crm_lead, self).copy(cr, uid, id, default, context=context)
+ return super(crm_lead, self).copy(cr, uid, id, default, context=local_context)
def get_empty_list_help(self, cr, uid, help, context=None):
+ context = dict(context or {})
context['empty_list_help_model'] = 'crm.case.section'
context['empty_list_help_id'] = context.get('default_section_id', None)
context['empty_list_help_document_name'] = _("opportunity")
diff --git a/addons/crm/crm_segmentation.py b/addons/crm/crm_segmentation.py
index a2ba28b2d7e..16c1588738f 100644
--- a/addons/crm/crm_segmentation.py
+++ b/addons/crm/crm_segmentation.py
@@ -41,7 +41,7 @@ added to partners that match the segmentation criterions after computation.'),
('running','Running')], 'Execution Status', readonly=True),
'partner_id': fields.integer('Max Partner ID processed'),
'segmentation_line': fields.one2many('crm.segmentation.line', \
- 'segmentation_id', 'Criteria', required=True),
+ 'segmentation_id', 'Criteria', required=True, copy=True),
'sales_purchase_active': fields.boolean('Use The Sales Purchase Rules', help='Check if you want to use this tab as part of the segmentation rule. If not checked, the criteria beneath will be ignored')
}
_defaults = {
@@ -57,13 +57,13 @@ added to partners that match the segmentation criterions after computation.'),
@param ids: List of Process continue’s IDs"""
partner_obj = self.pool.get('res.partner')
- categs = self.read(cr, uid, ids, ['categ_id', 'exclusif', 'partner_id',\
- 'sales_purchase_active', 'profiling_active'])
+ categs = self.read(cr, uid, ids, ['categ_id', 'exclusif', 'sales_purchase_active'])
for categ in categs:
if start:
if categ['exclusif']:
cr.execute('delete from res_partner_res_partner_category_rel \
where category_id=%s', (categ['categ_id'][0],))
+ partner_obj.invalidate_cache(cr, uid, ['category_id'])
id = categ['id']
@@ -86,6 +86,7 @@ added to partners that match the segmentation criterions after computation.'),
if categ['categ_id'][0] not in category_ids:
cr.execute('insert into res_partner_res_partner_category_rel (category_id,partner_id) \
values (%s,%s)', (categ['categ_id'][0], partner.id))
+ partner_obj.invalidate_cache(cr, uid, ['category_id'], [partner.id])
self.write(cr, uid, [id], {'state':'not running', 'partner_id':0})
return True
diff --git a/addons/crm/res_partner.py b/addons/crm/res_partner.py
index bb216e2040e..a8d5724acb1 100644
--- a/addons/crm/res_partner.py
+++ b/addons/crm/res_partner.py
@@ -53,14 +53,6 @@ class res_partner(osv.osv):
'phonecall_count': fields.function(_opportunity_meeting_phonecall_count, string="Phonecalls", type="integer", multi='opp_meet'),
}
- def copy(self, cr, uid, record_id, default=None, context=None):
- if default is None:
- default = {}
-
- default.update({'opportunity_ids': [], 'meeting_ids' : [], 'phonecall_ids' : []})
-
- return super(res_partner, self).copy(cr, uid, record_id, default, context)
-
def redirect_partner_form(self, cr, uid, partner_id, context=None):
search_view = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'view_res_partner_filter')
value = {
diff --git a/addons/crm/sales_team.py b/addons/crm/sales_team.py
index cd75417816d..ce15e1791a9 100644
--- a/addons/crm/sales_team.py
+++ b/addons/crm/sales_team.py
@@ -44,10 +44,10 @@ class crm_case_section(osv.Model):
help="The first contact you get with a potential customer is a lead you qualify before converting it into a real business opportunity. Check this box to manage leads in this sales team."),
'use_opportunities': fields.boolean('Opportunities', help="Check this box to manage opportunities in this sales team."),
'monthly_open_leads': fields.function(_get_opportunities_data,
- type="string", readonly=True, multi='_get_opportunities_data',
+ type="any", readonly=True, multi='_get_opportunities_data',
string='Open Leads per Month'),
'monthly_planned_revenue': fields.function(_get_opportunities_data,
- type="string", readonly=True, multi='_get_opportunities_data',
+ type="any", readonly=True, multi='_get_opportunities_data',
string='Planned Revenue per Month'),
'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True, help="The email address associated with this team. New emails received will automatically create new leads assigned to the team."),
}
diff --git a/addons/crm/test/lead2opportunity_assign_salesmen.yml b/addons/crm/test/lead2opportunity_assign_salesmen.yml
index 9ac3e954f88..2de9a237837 100644
--- a/addons/crm/test/lead2opportunity_assign_salesmen.yml
+++ b/addons/crm/test/lead2opportunity_assign_salesmen.yml
@@ -3,22 +3,22 @@
-
!record {model: res.users, id: test_res_user_01}:
name: 'Test user A'
- login: 'tua'
+ login: 'tua@example.com'
new_password: 'tua'
-
!record {model: res.users, id: test_res_user_02}:
name: 'Test user B'
- login: 'tub'
+ login: 'tub@example.com'
new_password: 'tub'
-
!record {model: res.users, id: test_res_user_03}:
name: 'Test user C'
- login: 'tuc'
+ login: 'tuc@example.com'
new_password: 'tuc'
-
!record {model: res.users, id: test_res_user_04}:
name: 'Test user D'
- login: 'tud'
+ login: 'tud@example.com'
new_password: 'tud'
-
Salesman also creates lead so giving access rights of salesman.
@@ -78,4 +78,4 @@
for opp in opps:
assert opp.type == 'opportunity', 'Type mismatch: this should be an opp, not a lead'
assert opp.user_id.id == salesmen_ids[i], 'Salesman mismatch: expected salesman %r, got %r' % (salesmen_ids[i], opp.user_id.id)
- i = i+1 if (i < len(salesmen_ids) - 1) else 0
\ No newline at end of file
+ i = i+1 if (i < len(salesmen_ids) - 1) else 0
diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py
index 7964e4a4a3a..e8372add327 100644
--- a/addons/crm/wizard/crm_lead_to_opportunity.py
+++ b/addons/crm/wizard/crm_lead_to_opportunity.py
@@ -158,7 +158,7 @@ class crm_lead2opportunity_partner(osv.osv_memory):
lead_ids = [lead_id]
lead = lead_obj.read(cr, uid, lead_id, ['type', 'user_id'], context=context)
if lead['type'] == "lead":
- context.update({'active_ids': lead_ids})
+ context = dict(context, active_ids=lead_ids)
self._convert_opportunity(cr, uid, ids, {'lead_ids': lead_ids, 'user_ids': [w.user_id.id], 'section_id': w.section_id.id}, context=context)
elif not context.get('no_force_assignation') or not lead['user_id']:
lead_obj.write(cr, uid, lead_id, {'user_id': w.user_id.id, 'section_id': w.section_id.id}, context=context)
diff --git a/addons/crm/wizard/crm_merge_opportunities.py b/addons/crm/wizard/crm_merge_opportunities.py
index 289266bfad3..b1f62b91ebe 100644
--- a/addons/crm/wizard/crm_merge_opportunities.py
+++ b/addons/crm/wizard/crm_merge_opportunities.py
@@ -39,8 +39,7 @@ class crm_merge_opportunity(osv.osv_memory):
}
def action_merge(self, cr, uid, ids, context=None):
- if context is None:
- context = {}
+ context = dict(context or {})
lead_obj = self.pool.get('crm.lead')
wizard = self.browse(cr, uid, ids[0], context=context)
diff --git a/addons/crm_claim/crm_claim.py b/addons/crm_claim/crm_claim.py
index cd4e6ad73b2..9cd25570704 100644
--- a/addons/crm_claim/crm_claim.py
+++ b/addons/crm_claim/crm_claim.py
@@ -153,8 +153,7 @@ class crm_claim(osv.osv):
return {'value': {'email_from': address.email, 'partner_phone': address.phone}}
def create(self, cr, uid, vals, context=None):
- if context is None:
- context = {}
+ context = dict(context or {})
if vals.get('section_id') and not context.get('default_section_id'):
context['default_section_id'] = vals.get('section_id')
diff --git a/addons/crm_partner_assign/crm_lead.py b/addons/crm_partner_assign/crm_lead.py
index d7463355271..c0dd061f511 100644
--- a/addons/crm_partner_assign/crm_lead.py
+++ b/addons/crm_partner_assign/crm_lead.py
@@ -31,7 +31,7 @@ class crm_lead(osv.osv):
model, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'crm_partner_assign', 'crm_lead_channel_interested_act')
except ValueError:
raise osv.except_osv(_('Error!'), _("The CRM Channel Interested Action is missing"))
- action = self.pool[model].read(cr, uid, action_id, context=context)
+ action = self.pool[model].read(cr, uid, [action_id], context=context)[0]
action_context = eval(action['context'])
action_context['interested'] = interested
action['context'] = str(action_context)
diff --git a/addons/crm_profiling/crm_profiling.py b/addons/crm_profiling/crm_profiling.py
index 5b1af306b5f..a8866e7639f 100644
--- a/addons/crm_profiling/crm_profiling.py
+++ b/addons/crm_profiling/crm_profiling.py
@@ -147,7 +147,7 @@ class question(osv.osv):
_columns={
'name': fields.char("Question", required=True),
- 'answers_ids': fields.one2many("crm_profiling.answer","question_id","Avalaible Answers",),
+ 'answers_ids': fields.one2many("crm_profiling.answer", "question_id", "Available Answers", copy=True),
}
@@ -209,6 +209,7 @@ class partner(osv.osv):
if 'answers_ids' in vals:
vals['category_id']=[[6, 0, _recompute_categ(self, cr, uid, ids[0], vals['answers_ids'][0][2])]]
+
return super(partner, self).write(cr, uid, ids, vals, context=context)
@@ -248,6 +249,7 @@ class crm_segmentation(osv.osv):
if categ['exclusif']:
cr.execute('delete from res_partner_res_partner_category_rel where \
category_id=%s', (categ['categ_id'][0],))
+ partner_obj.invalidate_cache(cr, uid, ['category_id'])
id = categ['id']
@@ -281,6 +283,7 @@ class crm_segmentation(osv.osv):
category_ids = [categ_id.id for categ_id in partner.category_id]
if categ['categ_id'][0] not in category_ids:
cr.execute('insert into res_partner_res_partner_category_rel (category_id,partner_id) values (%s,%s)', (categ['categ_id'][0],partner.id))
+ partner_obj.invalidate_cache(cr, uid, ['category_id'], [partner.id])
self.write(cr, uid, [id], {'state':'not running', 'partner_id':0})
return True
diff --git a/addons/crm_profiling/wizard/open_questionnaire.py b/addons/crm_profiling/wizard/open_questionnaire.py
index 979e9809d09..b08939e3907 100644
--- a/addons/crm_profiling/wizard/open_questionnaire.py
+++ b/addons/crm_profiling/wizard/open_questionnaire.py
@@ -70,7 +70,7 @@ class open_questionnaire(osv.osv_memory):
result = models_data._get_id(cr, uid, 'crm_profiling', 'open_questionnaire_form')
res_id = models_data.browse(cr, uid, result, context=context).res_id
datas = self.browse(cr, uid, ids[0], context=context)
- context.update({'questionnaire_id': datas.questionnaire_id.id})
+ context = dict(context or {}, questionnaire_id=datas.questionnaire_id.id)
return {
'name': _('Questionnaire'),
diff --git a/addons/delivery/delivery.py b/addons/delivery/delivery.py
index 2ab2a10a3de..33db829ac80 100644
--- a/addons/delivery/delivery.py
+++ b/addons/delivery/delivery.py
@@ -178,7 +178,7 @@ class delivery_grid(osv.osv):
'state_ids': fields.many2many('res.country.state', 'delivery_grid_state_rel', 'grid_id', 'state_id', 'States'),
'zip_from': fields.char('Start Zip', size=12),
'zip_to': fields.char('To Zip', size=12),
- 'line_ids': fields.one2many('delivery.grid.line', 'grid_id', 'Grid Line'),
+ 'line_ids': fields.one2many('delivery.grid.line', 'grid_id', 'Grid Line', copy=True),
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the delivery grid without removing it."),
}
_defaults = {
diff --git a/addons/document/document.py b/addons/document/document.py
index d611539a00a..96868d3d66b 100644
--- a/addons/document/document.py
+++ b/addons/document/document.py
@@ -562,6 +562,7 @@ class document_storage(osv.osv):
# to write the fname and size, and update them in the db concurrently.
# We cannot use a write() here, because we are already in one.
cr.execute('UPDATE ir_attachment SET file_size = %s, index_content = %s, file_type = %s WHERE id = %s', (filesize, icont_u, mime, file_node.file_id))
+ self.pool.get('ir.attachment').invalidate_cache(cr, uid, ['file_size', 'index_content', 'file_type'], [file_node.file_id], context=context)
file_node.content_length = filesize
file_node.content_type = mime
return True
@@ -1114,7 +1115,7 @@ class node_dir(node_database):
if not self.check_perms('u'):
raise IOError(errno.EPERM,"Permission denied.")
- if directory._table_name=='document.directory':
+ if directory._name == 'document.directory':
if self.children(cr):
raise OSError(39, 'Directory not empty.')
res = self.context._dirobj.unlink(cr, uid, [directory.id])
@@ -1694,7 +1695,7 @@ class node_file(node_class):
return False
document = document_obj.browse(cr, uid, self.file_id, context=self.context.context)
res = False
- if document and document._table_name == 'ir.attachment':
+ if document and document._name == 'ir.attachment':
res = document_obj.unlink(cr, uid, [document.id])
return res
diff --git a/addons/edi/models/edi.py b/addons/edi/models/edi.py
index 1b9e0c4df08..43c4dfcafb5 100644
--- a/addons/edi/models/edi.py
+++ b/addons/edi/models/edi.py
@@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Business Applications
-# Copyright (c) 2011-2012 OpenERP S.A.
+# Copyright (c) 2011-2014 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
@@ -65,8 +65,8 @@ def last_update_for(record):
"""Returns the last update timestamp for the given record,
if available, otherwise False
"""
- if record._model._log_access:
- record_log = record.perm_read()[0]
+ if record._log_access:
+ record_log = record.get_metadata()[0]
return record_log.get('write_date') or record_log.get('create_date') or False
return False
diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py
index 90695bed829..128e5928f70 100644
--- a/addons/email_template/email_template.py
+++ b/addons/email_template/email_template.py
@@ -30,7 +30,7 @@ import urlparse
import openerp
from openerp import SUPERUSER_ID
from openerp.osv import osv, fields
-from openerp import tools
+from openerp import tools, api
from openerp.tools.translate import _
from urllib import urlencode, quote as quote
@@ -151,6 +151,7 @@ class email_template(osv.osv):
"""
if context is None:
context = {}
+ res_ids = filter(None, res_ids) # to avoid browsing [None] below
results = dict.fromkeys(res_ids, u"")
# try to load the template
@@ -250,10 +251,10 @@ class email_template(osv.osv):
help="Name to use for the generated report file (may contain placeholders)\n"
"The extension can be omitted and will then come from the report type."),
'report_template': fields.many2one('ir.actions.report.xml', 'Optional report to print and attach'),
- 'ref_ir_act_window': fields.many2one('ir.actions.act_window', 'Sidebar action', readonly=True,
+ 'ref_ir_act_window': fields.many2one('ir.actions.act_window', 'Sidebar action', readonly=True, copy=False,
help="Sidebar action to make this template available on records "
"of the related document model"),
- 'ref_ir_value': fields.many2one('ir.values', 'Sidebar Button', readonly=True,
+ 'ref_ir_value': fields.many2one('ir.values', 'Sidebar Button', readonly=True, copy=False,
help="Sidebar button to open the sidebar action"),
'attachment_ids': fields.many2many('ir.attachment', 'email_template_attachment_rel', 'email_template_id',
'attachment_id', 'Attachments',
@@ -334,13 +335,8 @@ class email_template(osv.osv):
def copy(self, cr, uid, id, default=None, context=None):
template = self.browse(cr, uid, id, context=context)
- if default is None:
- default = {}
- default = default.copy()
- default.update(
- name=_("%s (copy)") % (template.name),
- ref_ir_act_window=False,
- ref_ir_value=False)
+ default = dict(default or {},
+ name=_("%s (copy)") % template.name)
return super(email_template, self).copy(cr, uid, id, default, context)
def build_expression(self, field_name, sub_field_name, null_value):
@@ -505,6 +501,7 @@ class email_template(osv.osv):
return results
+ @api.cr_uid_id_context
def send_mail(self, cr, uid, template_id, res_id, force_send=False, raise_exception=False, context=None):
"""Generates a new mail message for the given template and record,
and schedules it for delivery through the ``mail`` module's scheduler.
@@ -541,6 +538,7 @@ class email_template(osv.osv):
'res_model': 'mail.message',
'res_id': mail.mail_message_id.id,
}
+ context = dict(context)
context.pop('default_type', None)
attachment_ids.append(ir_attachment.create(cr, uid, attachment_data, context=context))
if attachment_ids:
diff --git a/addons/email_template/ir_actions.py b/addons/email_template/ir_actions.py
index ae2395e3b81..3e7d2c1c67a 100644
--- a/addons/email_template/ir_actions.py
+++ b/addons/email_template/ir_actions.py
@@ -63,7 +63,7 @@ class actions_server(osv.Model):
""" Render the raw template in the server action fields. """
fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to']
if template_id:
- template_values = self.pool.get('email.template').read(cr, uid, template_id, fields, context)
+ template_values = self.pool.get('email.template').read(cr, uid, [template_id], fields, context)[0]
values = dict((field, template_values[field]) for field in fields if template_values.get(field))
if not values.get('email_from'):
return {'warning': {'title': 'Incomplete template', 'message': 'Your template should define email_from'}, 'value': values}
diff --git a/addons/email_template/tests/test_mail.py b/addons/email_template/tests/test_mail.py
index f88d11ec71d..f5a99137351 100644
--- a/addons/email_template/tests/test_mail.py
+++ b/addons/email_template/tests/test_mail.py
@@ -202,7 +202,7 @@ class test_message_compose(TestMail):
mail_value = mail_compose.generate_email_for_composer(cr, uid, email_template_id, uid)
self.assertEqual(set(mail_value['partner_ids']), set(send_to), 'mail.message partner_ids list created by template is incorrect')
- @mute_logger('openerp.osv.orm', 'openerp.osv.orm')
+ @mute_logger('openerp.models')
def test_10_email_templating(self):
""" Tests designed for the mail.compose.message wizard updated by email_template. """
cr, uid, context = self.cr, self.uid, {}
diff --git a/addons/email_template/wizard/mail_compose_message.py b/addons/email_template/wizard/mail_compose_message.py
index 7d148c87e3c..ebe66757b09 100644
--- a/addons/email_template/wizard/mail_compose_message.py
+++ b/addons/email_template/wizard/mail_compose_message.py
@@ -113,7 +113,7 @@ class mail_compose_message(osv.TransientModel):
}
values.setdefault('attachment_ids', list()).append(ir_attach_obj.create(cr, uid, data_attach, context=context))
else:
- values = self.default_get(cr, uid, ['subject', 'body', 'email_from', 'email_to', 'email_cc', 'partner_to', 'reply_to', 'attachment_ids', 'mail_server_id'], context=context)
+ values = self.default_get(cr, uid, ['subject', 'body', 'email_from', 'reply_to', 'attachment_ids', 'mail_server_id'], context=context)
if values.get('body_html'):
values['body'] = values.pop('body_html')
diff --git a/addons/event/event.py b/addons/event/event.py
index 9d7e878688f..2fb831b66f3 100644
--- a/addons/event/event.py
+++ b/addons/event/event.py
@@ -18,403 +18,365 @@
# along with this program. If not, see .
#
##############################################################################
-import pytz
-from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
-from datetime import datetime, timedelta
-from openerp.osv import fields, osv
-from openerp.tools.translate import _
-from openerp import SUPERUSER_ID
+from datetime import timedelta
-class event_type(osv.osv):
+import pytz
+
+from openerp import models, fields, api, _
+from openerp.exceptions import Warning
+
+class event_type(models.Model):
""" Event Type """
_name = 'event.type'
- _description = __doc__
- _columns = {
- 'name': fields.char('Event Type', required=True),
- 'default_reply_to': fields.char('Default Reply-To', size=64, help="The email address of the organizer which is put in the 'Reply-To' of all emails sent automatically at event or registrations confirmation. You can also put your email address of your mail gateway if you use one." ),
- 'default_email_event': fields.many2one('email.template','Event Confirmation Email', help="It will select this default confirmation event mail value when you choose this event"),
- 'default_email_registration': fields.many2one('email.template','Registration Confirmation Email', help="It will select this default confirmation registration mail value when you choose this event"),
- 'default_registration_min': fields.integer('Default Minimum Registration', help="It will select this default minimum value when you choose this event"),
- 'default_registration_max': fields.integer('Default Maximum Registration', help="It will select this default maximum value when you choose this event"),
- }
- _defaults = {
- 'default_registration_min': 0,
- 'default_registration_max': 0,
- }
-class event_event(osv.osv):
+ name = fields.Char(string='Event Type', required=True)
+ default_reply_to = fields.Char(string='Default Reply-To',
+ help="The email address of the organizer which is put in the 'Reply-To' of all emails sent automatically at event or registrations confirmation. You can also put your email address of your mail gateway if you use one.")
+ default_email_event = fields.Many2one('email.template', string='Event Confirmation Email',
+ help="It will select this default confirmation event mail value when you choose this event")
+ default_email_registration = fields.Many2one('email.template', string='Registration Confirmation Email',
+ help="It will select this default confirmation registration mail value when you choose this event")
+ default_registration_min = fields.Integer(string='Default Minimum Registration', default=0,
+ help="It will select this default minimum value when you choose this event")
+ default_registration_max = fields.Integer(string='Default Maximum Registration', default=0,
+ help="It will select this default maximum value when you choose this event")
+
+
+class event_event(models.Model):
"""Event"""
_name = 'event.event'
- _description = __doc__
- _order = 'date_begin'
_inherit = ['mail.thread', 'ir.needaction_mixin']
+ _order = 'date_begin'
- def name_get(self, cr, uid, ids, context=None):
- if not ids:
- return []
+ name = fields.Char(string='Event Name', translate=True, required=True,
+ readonly=False, states={'done': [('readonly', True)]})
+ user_id = fields.Many2one('res.users', string='Responsible User',
+ default=lambda self: self.env.user,
+ readonly=False, states={'done': [('readonly', True)]})
+ type = fields.Many2one('event.type', string='Type of Event',
+ readonly=False, states={'done': [('readonly', True)]})
+ seats_max = fields.Integer(string='Maximum Avalaible Seats', oldname='register_max',
+ readonly=True, states={'draft': [('readonly', False)]},
+ help="You can for each event define a maximum registration level. If you have too much registrations you are not able to confirm your event. (put 0 to ignore this rule )")
+ seats_min = fields.Integer(string='Minimum Reserved Seats', oldname='register_min',
+ readonly=True, states={'draft': [('readonly', False)]},
+ help="You can for each event define a minimum registration level. If you do not enough registrations you are not able to confirm your event. (put 0 to ignore this rule )")
- if isinstance(ids, (long, int)):
- ids = [ids]
+ seats_reserved = fields.Integer(oldname='register_current', string='Reserved Seats',
+ store=True, readonly=True, compute='_compute_seats')
+ seats_available = fields.Integer(oldname='register_avail', string='Available Seats',
+ store=True, readonly=True, compute='_compute_seats')
+ seats_unconfirmed = fields.Integer(oldname='register_prospect', string='Unconfirmed Seat Reservations',
+ store=True, readonly=True, compute='_compute_seats')
+ seats_used = fields.Integer(oldname='register_attended', string='Number of Participations',
+ store=True, readonly=True, compute='_compute_seats')
- res = []
- for record in self.browse(cr, uid, ids, context=context):
- date = record.date_begin.split(" ")[0]
- date_end = record.date_end.split(" ")[0]
- if date != date_end:
- date += ' - ' + date_end
- display_name = record.name + ' (' + date + ')'
- res.append((record['id'], display_name))
- return res
+ @api.multi
+ @api.depends('seats_max', 'registration_ids.state', 'registration_ids.nb_register')
+ def _compute_seats(self):
+ """ Determine reserved, available, reserved but unconfirmed and used seats. """
+ # initialize fields to 0
+ for event in self:
+ event.seats_unconfirmed = event.seats_reserved = event.seats_used = 0
+ # aggregate registrations by event and by state
+ if self.ids:
+ state_field = {
+ 'draft': 'seats_unconfirmed',
+ 'open':'seats_reserved',
+ 'done': 'seats_used',
+ }
+ query = """ SELECT event_id, state, sum(nb_register)
+ FROM event_registration
+ WHERE event_id IN %s AND state IN ('draft', 'open', 'done')
+ GROUP BY event_id, state
+ """
+ self._cr.execute(query, (tuple(self.ids),))
+ for event_id, state, num in self._cr.fetchall():
+ event = self.browse(event_id)
+ event[state_field[state]] += num
+ # compute seats_available
+ for event in self:
+ event.seats_available = \
+ event.seats_max - (event.seats_reserved + event.seats_used) \
+ if event.seats_max > 0 else 0
- def copy(self, cr, uid, id, default=None, context=None):
- """ Reset the state and the registrations while copying an event
- """
- if not default:
- default = {}
- default.update({
- 'state': 'draft',
- 'registration_ids': False,
- })
- return super(event_event, self).copy(cr, uid, id, default=default, context=context)
+ registration_ids = fields.One2many('event.registration', 'event_id', string='Registrations',
+ readonly=False, states={'done': [('readonly', True)]})
+ count_registrations = fields.Integer(string='Registrations',
+ compute='_count_registrations')
- def button_draft(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state': 'draft'}, context=context)
+ date_begin = fields.Datetime(string='Start Date', required=True,
+ readonly=True, states={'draft': [('readonly', False)]})
+ date_end = fields.Datetime(string='End Date', required=True,
+ readonly=True, states={'draft': [('readonly', False)]})
- def button_cancel(self, cr, uid, ids, context=None):
- registration = self.pool.get('event.registration')
- reg_ids = registration.search(cr, uid, [('event_id','in',ids)], context=context)
- for event_reg in registration.browse(cr,uid,reg_ids,context=context):
- if event_reg.state == 'done':
- raise osv.except_osv(_('Error!'),_("You have already set a registration for this event as 'Attended'. Please reset it to draft if you want to cancel this event.") )
- registration.write(cr, uid, reg_ids, {'state': 'cancel'}, context=context)
- return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
-
- def button_done(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state': 'done'}, context=context)
-
- def confirm_event(self, cr, uid, ids, context=None):
- register_pool = self.pool.get('event.registration')
- for event in self.browse(cr, uid, ids, context=context):
- if event.email_confirmation_id:
- #send reminder that will confirm the event for all the people that were already confirmed
- reg_ids = register_pool.search(cr, uid, [
- ('event_id', '=', event.id),
- ('state', 'not in', ['draft', 'cancel'])], context=context)
- register_pool.mail_user_confirm(cr, uid, reg_ids)
- return self.write(cr, uid, ids, {'state': 'confirm'}, context=context)
-
- def button_confirm(self, cr, uid, ids, context=None):
- """ Confirm Event and send confirmation email to all register peoples
- """
- return self.confirm_event(cr, uid, isinstance(ids, (int, long)) and [ids] or ids, context=context)
-
- def _get_seats(self, cr, uid, ids, fields, args, context=None):
- """Get reserved, available, reserved but unconfirmed and used seats.
- @return: Dictionary of function field values.
- """
- keys = {'draft': 'seats_unconfirmed', 'open':'seats_reserved', 'done': 'seats_used'}
- res = {}
- for event_id in ids:
- res[event_id] = {key:0 for key in keys.values()}
- query = "SELECT state, sum(nb_register) FROM event_registration WHERE event_id = %s AND state IN ('draft','open','done') GROUP BY state"
- for event in self.pool.get('event.event').browse(cr, uid, ids, context=context):
- cr.execute(query, (event.id,))
- reg_states = cr.fetchall()
- for reg_state in reg_states:
- res[event.id][keys[reg_state[0]]] = reg_state[1]
- res[event.id]['seats_available'] = event.seats_max - \
- (res[event.id]['seats_reserved'] + res[event.id]['seats_used']) \
- if event.seats_max > 0 else None
- return res
-
- def _get_events_from_registrations(self, cr, uid, ids, context=None):
- """Get reserved, available, reserved but unconfirmed and used seats, of the event related to a registration.
- @return: Dictionary of function field values.
- """
- event_ids=set()
- for registration in self.pool['event.registration'].browse(cr, uid, ids, context=context):
- event_ids.add(registration.event_id.id)
- return list(event_ids)
-
- def _subscribe_fnc(self, cr, uid, ids, fields, args, context=None):
- """This functional fields compute if the current user (uid) is already subscribed or not to the event passed in parameter (ids)
- """
- register_pool = self.pool.get('event.registration')
- res = {}
- for event in self.browse(cr, uid, ids, context=context):
- res[event.id] = False
- curr_reg_id = register_pool.search(cr, uid, [('user_id', '=', uid), ('event_id', '=' ,event.id)])
- if curr_reg_id:
- for reg in register_pool.browse(cr, uid, curr_reg_id, context=context):
- if reg.state in ('open','done'):
- res[event.id]= True
- continue
- return res
-
- def _count_registrations(self, cr, uid, ids, field_name, arg, context=None):
- return {
- event.id: len(event.registration_ids)
- for event in self.browse(cr, uid, ids, context=context)
- }
-
- def _compute_date_tz(self, cr, uid, ids, fld, arg, context=None):
- if context is None:
- context = {}
- res = {}
- for event in self.browse(cr, uid, ids, context=context):
- ctx = dict(context, tz=(event.date_tz or 'UTC'))
- if fld == 'date_begin_located':
- date_to_convert = event.date_begin
- elif fld == 'date_end_located':
- date_to_convert = event.date_end
- res[event.id] = fields.datetime.context_timestamp(cr, uid, datetime.strptime(date_to_convert, DEFAULT_SERVER_DATETIME_FORMAT), context=ctx)
- return res
-
- def _tz_get(self, cr, uid, context=None):
+ @api.model
+ def _tz_get(self):
return [(x, x) for x in pytz.all_timezones]
- _columns = {
- 'name': fields.char('Event Name', required=True, translate=True, readonly=False, states={'done': [('readonly', True)]}),
- 'user_id': fields.many2one('res.users', 'Responsible User', readonly=False, states={'done': [('readonly', True)]}),
- 'type': fields.many2one('event.type', 'Type of Event', readonly=False, states={'done': [('readonly', True)]}),
- 'seats_max': fields.integer('Maximum Avalaible Seats', oldname='register_max', help="You can for each event define a maximum registration level. If you have too much registrations you are not able to confirm your event. (put 0 to ignore this rule )", readonly=True, states={'draft': [('readonly', False)]}),
- 'seats_min': fields.integer('Minimum Reserved Seats', oldname='register_min', help="You can for each event define a minimum registration level. If you do not enough registrations you are not able to confirm your event. (put 0 to ignore this rule )", readonly=True, states={'draft': [('readonly', False)]}),
- 'seats_reserved': fields.function(_get_seats, oldname='register_current', string='Reserved Seats', type='integer', multi='seats_reserved',
- store={'event.registration': (_get_events_from_registrations, ['state'], 10),
- 'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['seats_max', 'registration_ids'], 20)}),
- 'seats_available': fields.function(_get_seats, oldname='register_avail', string='Available Seats', type='integer', multi='seats_reserved',
- store={'event.registration': (_get_events_from_registrations, ['state'], 10),
- 'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['seats_max', 'registration_ids'], 20)}),
- 'seats_unconfirmed': fields.function(_get_seats, oldname='register_prospect', string='Unconfirmed Seat Reservations', type='integer', multi='seats_reserved',
- store={'event.registration': (_get_events_from_registrations, ['state'], 10),
- 'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['seats_max', 'registration_ids'], 20)}),
- 'seats_used': fields.function(_get_seats, oldname='register_attended', string='Number of Participations', type='integer', multi='seats_reserved',
- store={'event.registration': (_get_events_from_registrations, ['state'], 10),
- 'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['seats_max', 'registration_ids'], 20)}),
- 'registration_ids': fields.one2many('event.registration', 'event_id', 'Registrations', readonly=False, states={'done': [('readonly', True)]}),
- 'date_tz': fields.selection(_tz_get, string='Timezone'),
- 'date_begin': fields.datetime('Start Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
- 'date_end': fields.datetime('End Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
- 'date_begin_located': fields.function(_compute_date_tz, string='Start Date Located', type="datetime"),
- 'date_end_located': fields.function(_compute_date_tz, string='End Date Located', type="datetime"),
- 'state': fields.selection([
+ date_tz = fields.Selection('_tz_get', string='Timezone',
+ default=lambda self: self._context.get('tz', 'UTC'))
+
+ @api.one
+ @api.depends('date_tz', 'date_begin')
+ def _compute_date_begin_tz(self):
+ if self.date_begin:
+ self_in_tz = self.with_context(tz=(self.date_tz or 'UTC'))
+ date_begin = fields.Datetime.from_string(self.date_begin)
+ self.date_begin_located = fields.Datetime.to_string(fields.Datetime.context_timestamp(self_in_tz, date_begin))
+ else:
+ self.date_begin_located = False
+
+ @api.one
+ @api.depends('date_tz', 'date_end')
+ def _compute_date_end_tz(self):
+ if self.date_end:
+ self_in_tz = self.with_context(tz=(self.date_tz or 'UTC'))
+ date_end = fields.Datetime.from_string(self.date_end)
+ self.date_end_located = fields.Datetime.to_string(fields.Datetime.context_timestamp(self_in_tz, date_end))
+ else:
+ self.date_end_located = False
+
+ date_begin_located = fields.Datetime(string='Start Date Located', compute='_compute_date_begin_tz')
+ date_end_located = fields.Datetime(string='End Date Located', compute='_compute_date_end_tz')
+
+ state = fields.Selection([
('draft', 'Unconfirmed'),
('cancel', 'Cancelled'),
('confirm', 'Confirmed'),
- ('done', 'Done')],
- 'Status', readonly=True, required=True,
- help='If event is created, the status is \'Draft\'.If event is confirmed for the particular dates the status is set to \'Confirmed\'. If the event is over, the status is set to \'Done\'.If event is cancelled the status is set to \'Cancelled\'.'),
- 'email_registration_id' : fields.many2one('email.template','Registration Confirmation Email', help='This field contains the template of the mail that will be automatically sent each time a registration for this event is confirmed.'),
- 'email_confirmation_id' : fields.many2one('email.template','Event Confirmation Email', help="If you set an email template, each participant will receive this email announcing the confirmation of the event."),
- 'reply_to': fields.char('Reply-To Email', size=64, readonly=False, states={'done': [('readonly', True)]}, help="The email address of the organizer is likely to be put here, with the effect to be in the 'Reply-To' of the mails sent automatically at event or registrations confirmation. You can also put the email address of your mail gateway if you use one."),
- 'address_id': fields.many2one('res.partner','Location', readonly=False, states={'done': [('readonly', True)]}),
- 'country_id': fields.related('address_id', 'country_id',
- type='many2one', relation='res.country', string='Country', readonly=False, states={'done': [('readonly', True)]}, store=True),
- 'description': fields.html(
- 'Description', readonly=False, translate=True,
- states={'done': [('readonly', True)]},
- oldname='note'),
- 'company_id': fields.many2one('res.company', 'Company', required=False, change_default=True, readonly=False, states={'done': [('readonly', True)]}),
- 'is_subscribed' : fields.function(_subscribe_fnc, type="boolean", string='Subscribed'),
- 'organizer_id': fields.many2one('res.partner', "Organizer"),
- 'count_registrations': fields.function(_count_registrations, type="integer", string="Registrations"),
- }
- _defaults = {
- 'state': 'draft',
- 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'event.event', context=c),
- 'user_id': lambda obj, cr, uid, context: uid,
- 'organizer_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, context=c).company_id.partner_id.id,
- 'address_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, context=c).company_id.partner_id.id,
- 'date_tz': lambda self, cr, uid, ctx: ctx.get('tz', "UTC"),
- }
+ ('done', 'Done')
+ ], string='Status', default='draft', readonly=True, required=True, copy=False,
+ help="If event is created, the status is 'Draft'. If event is confirmed for the particular dates the status is set to 'Confirmed'. If the event is over, the status is set to 'Done'. If event is cancelled the status is set to 'Cancelled'.")
+ email_registration_id = fields.Many2one('email.template', string='Registration Confirmation Email',
+ help='This field contains the template of the mail that will be automatically sent each time a registration for this event is confirmed.')
+ email_confirmation_id = fields.Many2one('email.template', string='Event Confirmation Email',
+ help="If you set an email template, each participant will receive this email announcing the confirmation of the event.")
+ reply_to = fields.Char(string='Reply-To Email',
+ readonly=False, states={'done': [('readonly', True)]},
+ help="The email address of the organizer is likely to be put here, with the effect to be in the 'Reply-To' of the mails sent automatically at event or registrations confirmation. You can also put the email address of your mail gateway if you use one.")
+ address_id = fields.Many2one('res.partner', string='Location',
+ default=lambda self: self.env.user.company_id.partner_id,
+ readonly=False, states={'done': [('readonly', True)]})
+ country_id = fields.Many2one('res.country', string='Country', related='address_id.country_id',
+ store=True, readonly=False, states={'done': [('readonly', True)]})
+ description = fields.Html(string='Description', oldname='note', translate=True,
+ readonly=False, states={'done': [('readonly', True)]})
+ company_id = fields.Many2one('res.company', string='Company', change_default=True,
+ default=lambda self: self.env['res.company']._company_default_get('event.event'),
+ required=False, readonly=False, states={'done': [('readonly', True)]})
+ organizer_id = fields.Many2one('res.partner', string='Organizer',
+ default=lambda self: self.env.user.company_id.partner_id)
- def _check_seats_limit(self, cr, uid, ids, context=None):
- for event in self.browse(cr, uid, ids, context=context):
- if event.seats_max and event.seats_available < 0:
- return False
- return True
+ is_subscribed = fields.Boolean(string='Subscribed',
+ compute='_compute_subscribe')
- _constraints = [
- (_check_seats_limit, 'No more available seats.', ['registration_ids','seats_max']),
- ]
+ @api.one
+ @api.depends('registration_ids')
+ def _count_registrations(self):
+ self.count_registrations = len(self.registration_ids)
- def subscribe_to_event(self, cr, uid, ids, context=None):
- register_pool = self.pool.get('event.registration')
- user_pool = self.pool.get('res.users')
- num_of_seats = int(context.get('ticket', 1))
- user = user_pool.browse(cr, uid, uid, context=context)
- curr_reg_ids = register_pool.search(cr, uid, [('user_id', '=', user.id), ('event_id', '=' , ids[0])])
- #the subscription is done with SUPERUSER_ID because in case we share the kanban view, we want anyone to be able to subscribe
- if not curr_reg_ids:
- curr_reg_ids = [register_pool.create(cr, SUPERUSER_ID, {'event_id': ids[0] ,'email': user.email, 'name':user.name, 'user_id': user.id, 'nb_register': num_of_seats})]
+ @api.one
+ @api.depends('registration_ids.user_id', 'registration_ids.state')
+ def _compute_subscribe(self):
+ """ Determine whether the current user is already subscribed to any event in `self` """
+ user = self.env.user
+ self.is_subscribed = any(
+ reg.user_id == user and reg.state in ('open', 'done')
+ for reg in self.registration_ids
+ )
+
+ @api.one
+ @api.depends('name', 'date_begin', 'date_end')
+ def _compute_display_name(self):
+ dates = [dt.split(' ')[0] for dt in [self.date_begin, self.date_end] if dt]
+ dates = sorted(set(dates))
+ self.display_name = '%s (%s)' % (self.name, ' - '.join(dates))
+
+ @api.one
+ @api.constrains('seats_max', 'seats_available')
+ def _check_seats_limit(self):
+ if self.seats_max and self.seats_available < 0:
+ raise Warning(_('No more available seats.'))
+
+ @api.one
+ @api.constrains('date_begin', 'date_end')
+ def _check_closing_date(self):
+ if self.date_end < self.date_begin:
+ raise Warning(_('Closing Date cannot be set before Beginning Date.'))
+
+ @api.one
+ def button_draft(self):
+ self.state = 'draft'
+
+ @api.one
+ def button_cancel(self):
+ for event_reg in self.registration_ids:
+ if event_reg.state == 'done':
+ raise Warning(_("You have already set a registration for this event as 'Attended'. Please reset it to draft if you want to cancel this event."))
+ self.registration_ids.write({'state': 'cancel'})
+ self.state = 'cancel'
+
+ @api.one
+ def button_done(self):
+ self.state = 'done'
+
+ @api.one
+ def confirm_event(self):
+ if self.email_confirmation_id:
+ # send reminder that will confirm the event for all the people that were already confirmed
+ regs = self.registration_ids.filtered(lambda reg: reg.state not in ('draft', 'cancel'))
+ regs.mail_user_confirm()
+ self.state = 'confirm'
+
+ @api.one
+ def button_confirm(self):
+ """ Confirm Event and send confirmation email to all register peoples """
+ self.confirm_event()
+
+ @api.one
+ def subscribe_to_event(self):
+ """ Subscribe the current user to a given event """
+ user = self.env.user
+ num_of_seats = int(self._context.get('ticket', 1))
+ regs = self.registration_ids.filtered(lambda reg: reg.user_id == user)
+ # the subscription is done as SUPERUSER_ID because in case we share the
+ # kanban view, we want anyone to be able to subscribe
+ if not regs:
+ regs = regs.sudo().create({
+ 'event_id': self.id,
+ 'email': user.email,
+ 'name':user.name,
+ 'user_id': user.id,
+ 'nb_register': num_of_seats,
+ })
else:
- register_pool.write(cr, uid, curr_reg_ids, {'nb_register': num_of_seats}, context=context)
- return register_pool.confirm_registration(cr, SUPERUSER_ID, curr_reg_ids, context=context)
+ regs.write({'nb_register': num_of_seats})
+ regs.sudo().confirm_registration()
- def unsubscribe_to_event(self, cr, uid, ids, context=None):
- register_pool = self.pool.get('event.registration')
- #the unsubscription is done with SUPERUSER_ID because in case we share the kanban view, we want anyone to be able to unsubscribe
- curr_reg_ids = register_pool.search(cr, SUPERUSER_ID, [('user_id', '=', uid), ('event_id', '=', ids[0])])
- return register_pool.button_reg_cancel(cr, SUPERUSER_ID, curr_reg_ids, context=context)
+ @api.one
+ def unsubscribe_to_event(self):
+ """ Unsubscribe the current user from a given event """
+ # the unsubscription is done as SUPERUSER_ID because in case we share
+ # the kanban view, we want anyone to be able to unsubscribe
+ user = self.env.user
+ regs = self.sudo().registration_ids.filtered(lambda reg: reg.user_id == user)
+ regs.button_reg_cancel()
- def _check_closing_date(self, cr, uid, ids, context=None):
- for event in self.browse(cr, uid, ids, context=context):
- if event.date_end < event.date_begin:
- return False
- return True
+ @api.onchange('type')
+ def _onchange_type(self):
+ if self.type:
+ self.reply_to = self.type.default_reply_to
+ self.email_registration_id = self.type.default_email_registration
+ self.email_confirmation_id = self.type.default_email_event
+ self.seats_min = self.type.default_registration_min
+ self.seats_max = self.type.default_registration_max
- _constraints = [
- (_check_closing_date, 'Error ! Closing Date cannot be set before Beginning Date.', ['date_end']),
- ]
-
- def onchange_event_type(self, cr, uid, ids, type_event, context=None):
- values = {}
- if type_event:
- type_info = self.pool.get('event.type').browse(cr,uid,type_event,context)
- dic ={
- 'reply_to': type_info.default_reply_to,
- 'email_registration_id': type_info.default_email_registration.id,
- 'email_confirmation_id': type_info.default_email_event.id,
- 'seats_min': type_info.default_registration_min,
- 'seats_max': type_info.default_registration_max,
- }
- values.update(dic)
- return values
-
- def onchange_start_date(self, cr, uid, ids, date_begin=False, date_end=False, context=None):
- res = {'value':{}}
- if date_end:
- return res
- if date_begin and isinstance(date_begin, str):
- date_begin = datetime.strptime(date_begin, "%Y-%m-%d %H:%M:%S")
- date_end = date_begin + timedelta(hours=1)
- res['value'] = {'date_end': date_end.strftime("%Y-%m-%d %H:%M:%S")}
- return res
+ @api.onchange('date_begin')
+ def _onchange_date_begin(self):
+ if self.date_begin and not self.date_end:
+ date_begin = fields.Datetime.from_string(self.date_begin)
+ self.date_end = fields.Datetime.to_string(date_begin + timedelta(hours=1))
-class event_registration(osv.osv):
+class event_registration(models.Model):
"""Event Registration"""
_name= 'event.registration'
- _description = __doc__
_inherit = ['mail.thread', 'ir.needaction_mixin']
- _columns = {
- 'id': fields.integer('ID'),
- 'origin': fields.char('Source Document', readonly=True,help="Reference of the sales order which created the registration"),
- 'nb_register': fields.integer('Number of Participants', required=True, readonly=True, states={'draft': [('readonly', False)]}),
- 'event_id': fields.many2one('event.event', 'Event', required=True, readonly=True, states={'draft': [('readonly', False)]}),
- 'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)]}),
- 'create_date': fields.datetime('Creation Date' , readonly=True),
- 'date_closed': fields.datetime('Attended Date', readonly=True),
- 'date_open': fields.datetime('Registration Date', readonly=True),
- 'reply_to': fields.related('event_id','reply_to',string='Reply-to Email', type='char', readonly=True,),
- 'log_ids': fields.one2many('mail.message', 'res_id', 'Logs', domain=[('model','=',_name)]),
- 'event_end_date': fields.related('event_id','date_end', type='datetime', string="Event End Date", readonly=True),
- 'event_begin_date': fields.related('event_id', 'date_begin', type='datetime', string="Event Start Date", readonly=True),
- 'user_id': fields.many2one('res.users', 'User', states={'done': [('readonly', True)]}),
- 'company_id': fields.related('event_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True, states={'draft':[('readonly',False)]}),
- 'state': fields.selection([('draft', 'Unconfirmed'),
- ('cancel', 'Cancelled'),
- ('open', 'Confirmed'),
- ('done', 'Attended')], 'Status',
- readonly=True),
- 'email': fields.char('Email', size=64),
- 'phone': fields.char('Phone', size=64),
- 'name': fields.char('Name', select=True),
- }
- _defaults = {
- 'nb_register': 1,
- 'state': 'draft',
- }
_order = 'name, create_date desc'
+ origin = fields.Char(string='Source Document', readonly=True,
+ help="Reference of the sales order which created the registration")
+ nb_register = fields.Integer(string='Number of Participants', required=True, default=1,
+ readonly=True, states={'draft': [('readonly', False)]})
+ event_id = fields.Many2one('event.event', string='Event', required=True,
+ readonly=True, states={'draft': [('readonly', False)]})
+ partner_id = fields.Many2one('res.partner', string='Partner',
+ states={'done': [('readonly', True)]})
+ date_open = fields.Datetime(string='Registration Date', readonly=True)
+ date_closed = fields.Datetime(string='Attended Date', readonly=True)
+ reply_to = fields.Char(string='Reply-to Email', related='event_id.reply_to',
+ readonly=True)
+ log_ids = fields.One2many('mail.message', 'res_id', string='Logs',
+ domain=[('model', '=', _name)])
+ event_begin_date = fields.Datetime(string="Event Start Date", related='event_id.date_begin',
+ readonly=True)
+ event_end_date = fields.Datetime(string="Event End Date", related='event_id.date_end',
+ readonly=True)
+ user_id = fields.Many2one('res.users', string='User', states={'done': [('readonly', True)]})
+ company_id = fields.Many2one('res.company', string='Company', related='event_id.company_id',
+ store=True, readonly=True, states={'draft':[('readonly', False)]})
+ state = fields.Selection([
+ ('draft', 'Unconfirmed'),
+ ('cancel', 'Cancelled'),
+ ('open', 'Confirmed'),
+ ('done', 'Attended'),
+ ], string='Status', default='draft', readonly=True, copy=False)
+ email = fields.Char(string='Email')
+ phone = fields.Char(string='Phone')
+ name = fields.Char(string='Name', select=True)
- def _check_seats_limit(self, cr, uid, ids, context=None):
- for registration in self.browse(cr, uid, ids, context=context):
- if registration.event_id.seats_max and \
- registration.event_id.seats_available < (registration.state == 'draft' and registration.nb_register or 0):
- return False
- return True
+ @api.one
+ @api.constrains('event_id', 'state', 'nb_register')
+ def _check_seats_limit(self):
+ if self.event_id.seats_max and \
+ self.event_id.seats_available < (self.nb_register if self.state == 'draft' else 0):
+ raise Warning(_('No more available seats.'))
- _constraints = [
- (_check_seats_limit, 'No more available seats.', ['event_id','nb_register','state']),
- ]
+ @api.one
+ def do_draft(self):
+ self.state = 'draft'
- def do_draft(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state': 'draft'}, context=context)
+ @api.one
+ def confirm_registration(self):
+ self.event_id.message_post(
+ body=_('New registration confirmed: %s.') % (self.name or ''),
+ subtype="event.mt_event_registration")
+ self.message_post(body=_('Event Registration confirmed.'))
+ self.state = 'open'
- def confirm_registration(self, cr, uid, ids, context=None):
- for reg in self.browse(cr, uid, ids, context=context or {}):
- self.pool.get('event.event').message_post(cr, uid, [reg.event_id.id], body=_('New registration confirmed: %s.') % (reg.name or '', ),subtype="event.mt_event_registration", context=context)
- self.message_post(cr, uid, reg.id, body=_('Event Registration confirmed.'), context=context)
- return self.write(cr, uid, ids, {'state': 'open'}, context=context)
+ @api.one
+ def registration_open(self):
+ """ Open Registration """
+ self.confirm_registration()
+ self.mail_user()
- def registration_open(self, cr, uid, ids, context=None):
- """ Open Registration
- """
- res = self.confirm_registration(cr, uid, ids, context=context)
- self.mail_user(cr, uid, ids, context=context)
- return res
+ @api.one
+ def button_reg_close(self):
+ """ Close Registration """
+ today = fields.Datetime.now()
+ if self.event_id.date_begin <= today:
+ self.write({'state': 'done', 'date_closed': today})
+ else:
+ raise Warning(_("You must wait for the starting day of the event to do this action."))
- def button_reg_close(self, cr, uid, ids, context=None):
- """ Close Registration
- """
- if context is None:
- context = {}
- today = fields.datetime.now()
- for registration in self.browse(cr, uid, ids, context=context):
- if today >= registration.event_id.date_begin:
- values = {'state': 'done', 'date_closed': today}
- self.write(cr, uid, ids, values)
- else:
- raise osv.except_osv(_('Error!'), _("You must wait for the starting day of the event to do this action."))
- return True
+ @api.one
+ def button_reg_cancel(self):
+ self.state = 'cancel'
- def button_reg_cancel(self, cr, uid, ids, context=None, *args):
- return self.write(cr, uid, ids, {'state': 'cancel'})
+ @api.one
+ def mail_user(self):
+ """Send email to user with email_template when registration is done """
+ if self.event_id.state == 'confirm' and self.event_id.email_confirmation_id:
+ self.mail_user_confirm()
+ else:
+ template = self.event_id.email_registration_id
+ if template:
+ mail_message = template.send_mail(self.id)
- def mail_user(self, cr, uid, ids, context=None):
- """
- Send email to user with email_template when registration is done
- """
- for registration in self.browse(cr, uid, ids, context=context):
- if registration.event_id.state == 'confirm' and registration.event_id.email_confirmation_id.id:
- self.mail_user_confirm(cr, uid, ids, context=context)
- else:
- template_id = registration.event_id.email_registration_id.id
- if template_id:
- mail_message = self.pool.get('email.template').send_mail(cr,uid,template_id,registration.id)
- return True
+ @api.one
+ def mail_user_confirm(self):
+ """Send email to user when the event is confirmed """
+ template = self.event_id.email_confirmation_id
+ if template:
+ mail_message = template.send_mail(self.id)
- def mail_user_confirm(self, cr, uid, ids, context=None):
- """
- Send email to user when the event is confirmed
- """
- for registration in self.browse(cr, uid, ids, context=context):
- template_id = registration.event_id.email_confirmation_id.id
- if template_id:
- mail_message = self.pool.get('email.template').send_mail(cr,uid,template_id,registration.id)
- return True
-
- def onchange_contact_id(self, cr, uid, ids, contact, partner, context=None):
- if not contact:
- return {}
- addr_obj = self.pool.get('res.partner')
- contact_id = addr_obj.browse(cr, uid, contact, context=context)
- return {'value': {
- 'email':contact_id.email,
- 'name':contact_id.name,
- 'phone':contact_id.phone,
- }}
-
- def onchange_partner_id(self, cr, uid, ids, part, context=None):
- res_obj = self.pool.get('res.partner')
- data = {}
- if not part:
- return {'value': data}
- addr = res_obj.address_get(cr, uid, [part]).get('default', False)
- if addr:
- d = self.onchange_contact_id(cr, uid, ids, addr, part, context)
- data.update(d['value'])
- return {'value': data}
+ @api.onchange('partner_id')
+ def _onchange_partner(self):
+ if self.partner_id:
+ contact = self.partner_id.address_get().get('default', False)
+ if contact:
+ self.name = contact.name
+ self.email = contact.email
+ self.phone = contact.phone
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml
index 1ae4d9c449e..73cbb7b0d1b 100644
--- a/addons/event/event_view.xml
+++ b/addons/event/event_view.xml
@@ -86,8 +86,8 @@
-
-
+
+
@@ -117,7 +117,7 @@
-
+
@@ -366,7 +366,7 @@
-
+
diff --git a/addons/event/report/report_event_registration.py b/addons/event/report/report_event_registration.py
index c4a4dfd6b2d..7eb1d122846 100644
--- a/addons/event/report/report_event_registration.py
+++ b/addons/event/report/report_event_registration.py
@@ -19,34 +19,32 @@
#
##############################################################################
-from openerp.osv import fields, osv
+from openerp import models, fields
from openerp import tools
-class report_event_registration(osv.osv):
+
+class report_event_registration(models.Model):
+ """Events Analysis"""
_name = "report.event.registration"
- _description = "Events Analysis"
- _auto = False
- _columns = {
- 'event_date': fields.datetime('Event Date', readonly=True),
- 'event_id': fields.many2one('event.event', 'Event', required=True),
- 'draft_state': fields.integer(' # No of Draft Registrations', size=20),
- 'confirm_state': fields.integer(' # No of Confirmed Registrations', size=20),
- 'seats_max': fields.integer('Max Seats'),
- 'nbevent': fields.integer('Number of Registrations'),
- 'event_type': fields.many2one('event.type', 'Event Type'),
- 'registration_state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Attended'), ('cancel', 'Cancelled')], 'Registration State', readonly=True, required=True),
- 'event_state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled')], 'Event State', readonly=True, required=True),
- 'user_id': fields.many2one('res.users', 'Event Responsible', readonly=True),
- 'user_id_registration': fields.many2one('res.users', 'Register', readonly=True),
- 'name_registration': fields.char('Participant / Contact Name',size=45, readonly=True),
- 'company_id': fields.many2one('res.company', 'Company', readonly=True),
- }
_order = 'event_date desc'
+ _auto = False
+
+ event_date = fields.Datetime('Event Date', readonly=True)
+ event_id = fields.Many2one('event.event', 'Event', required=True)
+ draft_state = fields.Integer(' # No of Draft Registrations')
+ confirm_state = fields.Integer(' # No of Confirmed Registrations')
+ seats_max = fields.Integer('Max Seats')
+ nbevent = fields.Integer('Number of Registrations')
+ event_type = fields.Many2one('event.type', 'Event Type')
+ registration_state = fields.Selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Attended'), ('cancel', 'Cancelled')], 'Registration State', readonly=True, required=True)
+ event_state = fields.Selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled')], 'Event State', readonly=True, required=True)
+ user_id = fields.Many2one('res.users', 'Event Responsible', readonly=True)
+ user_id_registration = fields.Many2one('res.users', 'Register', readonly=True)
+ name_registration = fields.Char('Participant / Contact Name', readonly=True)
+ company_id = fields.Many2one('res.company', 'Company', readonly=True)
def init(self, cr):
- """
- Initialize the sql view for the event registration
- """
+ """Initialize the sql view for the event registration """
tools.drop_view_if_exists(cr, 'report_event_registration')
# TOFIX this request won't select events that have no registration
@@ -87,5 +85,4 @@ class report_event_registration(osv.osv):
)
""")
-
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/event/res_partner.py b/addons/event/res_partner.py
index b51e1fbb9d3..c81569e0ad2 100644
--- a/addons/event/res_partner.py
+++ b/addons/event/res_partner.py
@@ -19,15 +19,12 @@
#
##############################################################################
-from openerp.osv import fields, osv
+from openerp import models, fields
-class res_partner(osv.osv):
+class res_partner(models.Model):
_inherit = 'res.partner'
- _columns = {
- 'speaker': fields.boolean('Speaker', help="Check this box if this contact is a speaker."),
- }
-
+ speaker = fields.Boolean(help="Check this box if this contact is a speaker.")
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/event/test/process/event_draft2done.yml b/addons/event/test/process/event_draft2done.yml
index e1ad32ab8da..0cec9c3b16a 100644
--- a/addons/event/test/process/event_draft2done.yml
+++ b/addons/event/test/process/event_draft2done.yml
@@ -4,128 +4,126 @@
!context
uid: 'res_users_eventuser'
-
- I want to organize an event,
- into this conference I should create two registration.
+ I want to organize an event, into this conference I should create two registration.
One confirmed and attended the event and the other cancelled.
I create an event
-
!record {model: event.event, id: event_event}:
- name: event
- date_begin: 2012-01-01 19:05:15
- date_end: 2012-01-01 20:05:15
- seats_max: 10
+ name: event
+ date_begin: 2012-01-01 19:05:15
+ date_end: 2012-01-01 20:05:15
+ seats_max: 10
-
I create a registration for the event
-
!record {model: event.registration, id: event_registration1}:
- event_id: event_event
- name: test_confirm
- nb_register: 10
+ event_id: event_event
+ name: test_confirm
+ nb_register: 10
-
I create a second registration for the same event
-
- !record {model: event.registration, id: event_registration2}:
+ !record {model: event.registration, id: event_registration2}:
event_id: event_event
name: test_cancel
nb_register: 5
-
I check if the number of draft registrations in the event view is correct
-
- !assert {model: event.event, id: event_event ,string: 'the number of draft registration is not correct'}:
- - seats_unconfirmed == 15.00
+ !assert {model: event.event, id: event_event, string: 'the number of draft registration is not correct'}:
+ - seats_unconfirmed == 15.00
-
- I confirm the registration1
+ I confirm the registration1
-
- !python {model: event.registration}: |
- event_pool = self.pool.get("event.event")
- self.registration_open(cr, uid, [ref("event_registration1")],context=context)
+ !python {model: event.registration, id: event_registration1}: |
+ self.registration_open()
-
- I check that registration is "confirmed"
--
- !assert {model: event.registration, id: event_registration1}:
- - state == 'open', "registration should be confirmed."
--
- I check if the number of confirmed registration is correct
--
- !assert {model: event.event,id: event_event,string: 'the number of confirmed registration is not correct'}:
- - seats_reserved == 10.00
--
- I close the registration
--
- !python {model: event.registration}: |
- self.button_reg_close(cr, uid, [ref("event_registration1")],context=context)
--
- I check if registration is correctly finnished
+ I check that registration is "confirmed"
-
!assert {model: event.registration, id: event_registration1}:
- - state == 'done'
+ - state == 'open', "registration should be confirmed."
-
- I check if attended registration is correct
+ I check if the number of confirmed registration is correct
-
- !assert {model: event.event,id: event_event,string: 'the number of registration that attended the event is not correct'}:
- - seats_used == 10.00
+ !assert {model: event.event, id: event_event,string: 'the number of confirmed registration is not correct'}:
+ - seats_reserved == 10.00
-
- I cancel the second registration
+ I close the registration
-
- !python {model: event.registration}: |
- self.button_reg_cancel(cr, uid, [ref("event_registration2")],context=context)
+ !python {model: event.registration, id: event_registration1}: |
+ self.button_reg_close()
-
- I Check that registration is cancelled
+ I check if registration is correctly finnished
+-
+ !assert {model: event.registration, id: event_registration1}:
+ - state == 'done'
+-
+ I check if attended registration is correct
+-
+ !assert {model: event.event, id: event_event, string: 'the number of registration that attended the event is not correct'}:
+ - seats_used == 10.00
+-
+ I cancel the second registration
+-
+ !python {model: event.registration, id: event_registration2}: |
+ self.button_reg_cancel()
+-
+ I Check that registration is cancelled
-
!assert {model: event.registration, id: event_registration2}:
- state == 'cancel', "Registration should be cancelled."
-
- I confirm the event
+ I confirm the event
-
- !python {model: event.event}: |
- self.button_confirm(cr, uid, [ref("event_event")])
+ !python {model: event.event, id: event_event}: |
+ self.button_confirm()
-
- I check that event is in "confirmed" state.
+ I check that event is in "confirmed" state.
-
!assert {model: event.event, id: event_event}:
- state == 'confirm', "event should be confirmed."
-
- I close the event
+ I close the event
-
- !python {model: event.event}: |
- self.button_done(cr, uid, [ref("event_event")])
+ !python {model: event.event, id: event_event}: |
+ self.button_done()
-
- Check that event is in "close" state.
+ Check that event is in "close" state.
-
!assert {model: event.event, id: event_event}:
- state == 'done', "Event should be Closed."
-
- I reset event to draft
+ I reset event to draft
-
- !python {model: event.event}: |
- self.button_draft(cr, uid, [ref("event_event")])
+ !python {model: event.event, id: event_event}: |
+ self.button_draft()
-
- In order to test the cancellation of the event, I create a second event
+ In order to test the cancellation of the event, I create a second event
-
!record {model: event.event, id: event_event1}:
- name: event_cancel
- date_begin: 2012-01-01 19:05:15
- date_end: 2012-01-01 20:05:15
+ name: event_cancel
+ date_begin: 2012-01-01 19:05:15
+ date_end: 2012-01-01 20:05:15
-
I create a registration for the event
-
!record {model: event.registration, id: event_registration_cancel}:
- event_id: event_event1
- name: test_confirm_again
- nb_register: 5
+ event_id: event_event1
+ name: test_confirm_again
+ nb_register: 5
-
- Now I cancel this event
+ Now I cancel this event
-
- !python {model: event.event}: |
- self.button_cancel(cr, uid, [ref("event_event1")])
+ !python {model: event.event, id: event_event1}: |
+ self.button_cancel()
-
- I check if the event is cancelled
+ I check if the event is cancelled
-
!assert {model: event.event, id: event_event1}:
- - state == 'cancel', "Event should be cancelled."
+ - state == 'cancel', "Event should be cancelled."
-
- I check if its registrations are cancelled too
+ I check if its registrations are cancelled too
-
!assert {model: event.registration, id: event_registration_cancel}:
- - state == 'cancel', "Registration should be cancelled."
+ - state == 'cancel', "Registration should be cancelled."
diff --git a/addons/event/test/ui/demo_data.yml b/addons/event/test/ui/demo_data.yml
index 1a6631c93a3..19e8334f40c 100644
--- a/addons/event/test/ui/demo_data.yml
+++ b/addons/event/test/ui/demo_data.yml
@@ -11,21 +11,14 @@
!record {model: event.event, id: event_2}:
product_id: event_product_2
name: 'Conference on ERP Buisness'
--
- I call onchange event from event registration wizard.
--
- !python {model: partner.event.registration}: |
- context.update({'active_id': ref("base.res_partner_5")})
- self.onchange_event_id(cr, uid, 1, ref("event_1"), context=context)
-
I confirm event from wizard.
-
- !python {model: event.confirm}: |
- context.update({'event_ids': [ref("event_2")]})
- id = self.create(cr, uid , {})
- self.confirm(cr, uid, [id], context=context)
+ !python {model: event.confirm, id: False}: |
+ wizard = self.with_context(event_ids=[ref("event_2")]).create({})
+ wizard.confirm()
-
I call close registration process.
-
- !python {model: event.registration}: |
- self.button_reg_close(cr, uid, [ref("reg_0_2")])
+ !python {model: event.registration, id: reg_0_2}: |
+ self.button_reg_close()
diff --git a/addons/event/test/ui/duplicate_event.yml b/addons/event/test/ui/duplicate_event.yml
index c1287353c67..5c2dd85c3ed 100644
--- a/addons/event/test/ui/duplicate_event.yml
+++ b/addons/event/test/ui/duplicate_event.yml
@@ -1,16 +1,10 @@
-
Copy of event.
-
- !python {model: event.event}: |
- try:
- self.copy(cr, uid, ref("event_2"))
- except:
- pass
+ !python {model: event.event, id: event_2}: |
+ self.copy()
-
- Copy of event registarion.
+ Copy of event registration.
-
- !python {model: event.registration}: |
- try:
- self.copy(cr, uid, ref("reg_1_1"))
- except:
- pass
\ No newline at end of file
+ !python {model: event.registration, id: reg_1_1}: |
+ self.copy()
diff --git a/addons/event/wizard/event_confirm.py b/addons/event/wizard/event_confirm.py
index 5fb2cd67c87..a54b1d205a0 100644
--- a/addons/event/wizard/event_confirm.py
+++ b/addons/event/wizard/event_confirm.py
@@ -19,18 +19,17 @@
#
##############################################################################
-from openerp.osv import osv
+from openerp import models, api
-class event_confirm(osv.osv_memory):
- """
- Confirm Event
- """
+
+class event_confirm(models.TransientModel):
+ """Event Confirmation"""
_name = "event.confirm"
- _description = "Event Confirmation"
- def confirm(self, cr, uid, ids, context=None):
- self.pool.get('event.event').do_confirm(cr, uid, context.get('event_ids', []), context=context)
+ @api.multi
+ def confirm(self):
+ events = self.env['event.event'].browse(self._context.get('event_ids', []))
+ events.do_confirm()
return {'type': 'ir.actions.act_window_close'}
-
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/event_sale/event_sale.py b/addons/event_sale/event_sale.py
index aeaee98306a..a0940b5af2d 100644
--- a/addons/event_sale/event_sale.py
+++ b/addons/event_sale/event_sale.py
@@ -19,7 +19,8 @@
#
##############################################################################
-from openerp.addons.event.event import event_event as Event
+from openerp import api
+from openerp.fields import Integer, One2many, Html
from openerp.osv import fields, osv
from openerp.tools.translate import _
@@ -89,8 +90,7 @@ class sale_order_line(osv.osv):
'''
create registration with sales order
'''
- if context is None:
- context = {}
+ context = dict(context or {})
registration_obj = self.pool.get('event.registration')
for order_line in self.browse(cr, uid, ids, context=context):
if order_line.event_id:
@@ -123,62 +123,33 @@ class sale_order_line(osv.osv):
class event_event(osv.osv):
_inherit = 'event.event'
- def _get_seats_max(self, cr, uid, ids, field_name, arg, context=None):
- result = dict.fromkeys(ids, 0)
- for rec in self.browse(cr, uid, ids, context=context):
- result[rec.id] = sum([ticket.seats_max for ticket in rec.event_ticket_ids])
- return result
+ event_ticket_ids = One2many('event.event.ticket', 'event_id', string='Event Ticket',
+ default=lambda rec: rec._default_tickets())
+ seats_max = Integer(string='Maximum Available Seats',
+ help="The maximum registration level is equal to the sum of the maximum registration of event ticket. " +
+ "If you have too much registrations you are not able to confirm your event. (0 to ignore this rule )",
+ store=True, readonly=True, compute='_compute_seats_max')
- def _get_tickets(self, cr, uid, context={}):
+ badge_back = Html('Badge Back', translate=True, states={'done': [('readonly', True)]})
+ badge_innerleft = Html('Badge Innner Left', translate=True, states={'done': [('readonly', True)]})
+ badge_innerright = Html('Badge Inner Right', translate=True, states={'done': [('readonly', True)]})
+
+ @api.model
+ def _default_tickets(self):
try:
- product = self.pool.get('ir.model.data').get_object(cr, uid, 'event_sale', 'product_product_event')
+ product = self.env.ref('event_sale.product_product_event')
return [{
'name': _('Subscription'),
'product_id': product.id,
'price': 0,
}]
except ValueError:
- pass
- return []
+ return self.env['event.event.ticket']
- def _get_ticket_events(self, cr, uid, ids, context=None):
- # `self` is the event.event.ticket model when called by ORM!
- return list(set(ticket.event_id.id
- for ticket in self.browse(cr, uid, ids, context)))
-
- # proxy method, can't import parent method directly as unbound_method: it would receive
- # an invalid `self` when called by ORM
- def _events_from_registrations(self, cr, uid, ids, context=None):
- # `self` is the event.registration model when called by ORM
- return self.pool['event.event']._get_events_from_registrations(cr, uid, ids, context=context)
-
- _columns = {
- 'event_ticket_ids': fields.one2many('event.event.ticket', "event_id", "Event Ticket"),
- 'seats_max': fields.function(_get_seats_max,
- string='Maximum Avalaible Seats',
- help="The maximum registration level is equal to the sum of the maximum registration of event ticket." +
- "If you have too much registrations you are not able to confirm your event. (0 to ignore this rule )",
- type='integer',
- readonly=True,
- store={
- 'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['event_ticket_ids'], 20),
- 'event.event.ticket': (_get_ticket_events, ['seats_max'], 10),
- }),
- 'seats_available': fields.function(Event._get_seats, oldname='register_avail', string='Available Seats',
- type='integer', multi='seats_reserved',
- store={
- 'event.registration': (_events_from_registrations, ['state'], 10),
- 'event.event': (lambda self, cr, uid, ids, c = {}: ids,
- ['seats_max', 'registration_ids'], 20),
- 'event.event.ticket': (_get_ticket_events, ['seats_max'], 10),
- }),
- 'badge_back': fields.html('Badge Back', readonly=False, translate=True, states={'done': [('readonly', True)]}),
- 'badge_innerleft': fields.html('Badge Innner Left', readonly=False, translate=True, states={'done': [('readonly', True)]}),
- 'badge_innerright': fields.html('Badge Inner Right', readonly=False, translate=True, states={'done': [('readonly', True)]}),
- }
- _defaults = {
- 'event_ticket_ids': _get_tickets
- }
+ @api.one
+ @api.depends('event_ticket_ids.seats_max')
+ def _compute_seats_max(self):
+ self.seats_max = sum(ticket.seats_max for ticket in self.event_ticket_ids)
class event_ticket(osv.osv):
_name = 'event.event.ticket'
diff --git a/addons/fetchmail/fetchmail.py b/addons/fetchmail/fetchmail.py
index b4cac75b4a4..55ac185a0cb 100644
--- a/addons/fetchmail/fetchmail.py
+++ b/addons/fetchmail/fetchmail.py
@@ -52,7 +52,7 @@ class fetchmail_server(osv.osv):
'state':fields.selection([
('draft', 'Not Confirmed'),
('done', 'Confirmed'),
- ], 'Status', select=True, readonly=True),
+ ], 'Status', select=True, readonly=True, copy=False),
'server' : fields.char('Server Name', readonly=True, help="Hostname or IP of the mail server", states={'draft':[('readonly', False)]}),
'port' : fields.integer('Port', readonly=True, states={'draft':[('readonly', False)]}),
'type':fields.selection([
@@ -179,8 +179,7 @@ openerp_mailgate: "|/path/to/openerp-mailgate.py --host=localhost -u %(uid)d -p
def fetch_mail(self, cr, uid, ids, context=None):
"""WARNING: meant for cron usage only - will commit() after each email!"""
- if context is None:
- context = {}
+ context = dict(context or {})
context['fetchmail_cron_running'] = True
mail_thread = self.pool.get('mail.thread')
action_pool = self.pool.get('ir.actions.server')
diff --git a/addons/fleet/fleet.py b/addons/fleet/fleet.py
index 7dac086bb27..44dd9abc1f4 100644
--- a/addons/fleet/fleet.py
+++ b/addons/fleet/fleet.py
@@ -338,7 +338,7 @@ class fleet_vehicle(osv.Model):
'name': fields.function(_vehicle_name_get_fnc, type="char", string='Name', store=True),
'company_id': fields.many2one('res.company', 'Company'),
'license_plate': fields.char('License Plate', required=True, help='License plate number of the vehicle (ie: plate number for a car)'),
- 'vin_sn': fields.char('Chassis Number', help='Unique number written on the vehicle motor (VIN/SN number)'),
+ 'vin_sn': fields.char('Chassis Number', help='Unique number written on the vehicle motor (VIN/SN number)', copy=False),
'driver_id': fields.many2one('res.partner', 'Driver', help='Driver of the vehicle'),
'model_id': fields.many2one('fleet.vehicle.model', 'Model', required=True, help='Model of the vehicle'),
'log_fuel': fields.one2many('fleet.vehicle.log.fuel', 'vehicle_id', 'Fuel Logs'),
@@ -355,7 +355,7 @@ class fleet_vehicle(osv.Model):
'location': fields.char('Location', help='Location of the vehicle (garage, ...)'),
'seats': fields.integer('Seats Number', help='Number of seats of the vehicle'),
'doors': fields.integer('Doors Number', help='Number of doors of the vehicle'),
- 'tag_ids' :fields.many2many('fleet.vehicle.tag', 'fleet_vehicle_vehicle_tag_rel', 'vehicle_tag_id','tag_id', 'Tags'),
+ 'tag_ids' :fields.many2many('fleet.vehicle.tag', 'fleet_vehicle_vehicle_tag_rel', 'vehicle_tag_id','tag_id', 'Tags', copy=False),
'odometer': fields.function(_get_odometer, fnct_inv=_set_odometer, type='float', string='Last Odometer', help='Odometer measure of the vehicle at the moment of this log'),
'odometer_unit': fields.selection([('kilometers', 'Kilometers'),('miles','Miles')], 'Odometer Unit', help='Unit of the odometer ',required=True),
'transmission': fields.selection([('manual', 'Manual'), ('automatic', 'Automatic')], 'Transmission', help='Transmission Used by the vehicle'),
@@ -380,18 +380,6 @@ class fleet_vehicle(osv.Model):
'state_id': _get_default_state,
}
- def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({
- 'log_fuel':[],
- 'log_contracts':[],
- 'log_services':[],
- 'tag_ids':[],
- 'vin_sn':'',
- })
- return super(fleet_vehicle, self).copy(cr, uid, id, default, context=context)
-
def on_change_model(self, cr, uid, ids, model_id, context=None):
if not model_id:
return {}
@@ -403,9 +391,7 @@ class fleet_vehicle(osv.Model):
}
def create(self, cr, uid, data, context=None):
- if not context:
- context = {}
- context.update({'mail_create_nolog': True})
+ context = dict(context or {}, mail_create_nolog=True)
vehicle_id = super(fleet_vehicle, self).create(cr, uid, data, context=context)
vehicle = self.browse(cr, uid, vehicle_id, context=context)
self.message_post(cr, uid, [vehicle_id], body=_('%s %s has been added to the fleet!') % (vehicle.model_id.name,vehicle.license_plate), context=context)
@@ -803,12 +789,14 @@ class fleet_vehicle_log_contract(osv.Model):
'days_left': fields.function(get_days_left, type='integer', string='Warning Date'),
'insurer_id' :fields.many2one('res.partner', 'Supplier'),
'purchaser_id': fields.many2one('res.partner', 'Contractor', help='Person to which the contract is signed for'),
- 'ins_ref': fields.char('Contract Reference', size=64),
- 'state': fields.selection([('open', 'In Progress'), ('toclose','To Close'), ('closed', 'Terminated')], 'Status', readonly=True, help='Choose wheter the contract is still valid or not'),
- 'notes': fields.text('Terms and Conditions', help='Write here all supplementary informations relative to this contract'),
+ 'ins_ref': fields.char('Contract Reference', size=64, copy=False),
+ 'state': fields.selection([('open', 'In Progress'), ('toclose','To Close'), ('closed', 'Terminated')],
+ 'Status', readonly=True, help='Choose wheter the contract is still valid or not',
+ copy=False),
+ 'notes': fields.text('Terms and Conditions', help='Write here all supplementary informations relative to this contract', copy=False),
'cost_generated': fields.float('Recurring Cost Amount', help="Costs paid at regular intervals, depending on the cost frequency. If the cost frequency is set to unique, the cost will be logged at the start date"),
'cost_frequency': fields.selection([('no','No'), ('daily', 'Daily'), ('weekly','Weekly'), ('monthly','Monthly'), ('yearly','Yearly')], 'Recurring Cost Frequency', help='Frequency of the recuring cost', required=True),
- 'generated_cost_ids': fields.one2many('fleet.vehicle.cost', 'contract_id', 'Generated Costs', ondelete='cascade'),
+ 'generated_cost_ids': fields.one2many('fleet.vehicle.cost', 'contract_id', 'Generated Costs'),
'sum_cost': fields.function(_get_sum_cost, type='float', string='Indicative Costs Total'),
'cost_id': fields.many2one('fleet.vehicle.cost', 'Cost', required=True, ondelete='cascade'),
'cost_amount': fields.related('cost_id', 'amount', string='Amount', type='float', store=True), #we need to keep this field as a related with store=True because the graph view doesn't support (1) to address fields from inherited table and (2) fields that aren't stored in database
@@ -824,18 +812,6 @@ class fleet_vehicle_log_contract(osv.Model):
'cost_type': 'contract',
}
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- today = fields.date.context_today(self, cr, uid, context=context)
- default['date'] = today
- default['start_date'] = today
- default['expiration_date'] = self.compute_next_year_date(today)
- default['ins_ref'] = ''
- default['state'] = 'open'
- default['notes'] = ''
- return super(fleet_vehicle_log_contract, self).copy(cr, uid, id, default, context=context)
-
def contract_close(self, cr, uid, ids, context=None):
return self.write(cr, uid, ids, {'state': 'closed'}, context=context)
diff --git a/addons/gamification/models/challenge.py b/addons/gamification/models/challenge.py
index faed62f8da9..51e58bdc71b 100644
--- a/addons/gamification/models/challenge.py
+++ b/addons/gamification/models/challenge.py
@@ -124,7 +124,7 @@ class gamification_challenge(osv.Model):
('draft', 'Draft'),
('inprogress', 'In Progress'),
('done', 'Done'),
- ],
+ ], copy=False,
string='State', required=True, track_visibility='onchange'),
'manager_id': fields.many2one('res.users',
string='Responsible', help="The user responsible for the challenge."),
@@ -155,7 +155,7 @@ class gamification_challenge(osv.Model):
'line_ids': fields.one2many('gamification.challenge.line', 'challenge_id',
string='Lines',
help="List of goals that will be set",
- required=True),
+ required=True, copy=True),
'reward_id': fields.many2one('gamification.badge', string="For Every Succeding User"),
'reward_first_id': fields.many2one('gamification.badge', string="For 1st user"),
@@ -283,7 +283,7 @@ class gamification_challenge(osv.Model):
# in cron mode, will do intermediate commits
# TODO in trunk: replace by parameter
- context.update({'commit_gamification': True})
+ context = dict(context, commit_gamification=True)
return self._update_all(cr, uid, ids, context=context)
def _update_all(self, cr, uid, ids, context=None):
diff --git a/addons/gamification/models/goal.py b/addons/gamification/models/goal.py
index 041c6ae169c..ce5be513aef 100644
--- a/addons/gamification/models/goal.py
+++ b/addons/gamification/models/goal.py
@@ -424,8 +424,7 @@ class gamification_goal(osv.Model):
def create(self, cr, uid, vals, context=None):
"""Overwrite the create method to add a 'no_remind_goal' field to True"""
- if context is None:
- context = {}
+ context = dict(context or {})
context['no_remind_goal'] = True
return super(gamification_goal, self).create(cr, uid, vals, context=context)
diff --git a/addons/google_account/controllers/main.py b/addons/google_account/controllers/main.py
index 40843b6f4ba..2a3a4ae586f 100644
--- a/addons/google_account/controllers/main.py
+++ b/addons/google_account/controllers/main.py
@@ -1,8 +1,8 @@
import simplejson
import urllib
import openerp
-import openerp.addons.web.http as http
-from openerp.addons.web.http import request
+from openerp import http
+from openerp.http import request
import openerp.addons.web.controllers.main as webmain
from openerp.addons.web.http import SessionExpiredException
from werkzeug.exceptions import BadRequest
@@ -27,6 +27,6 @@ class google_auth(http.Controller):
elif kw.get('error'):
return werkzeug.utils.redirect("%s%s%s" % (url_return ,"?error=" , kw.get('error')))
else:
- return werkzeug.utils.redirect("%s%s%s" % (url_return ,"?error=Unknown_error"))
+ return werkzeug.utils.redirect("%s%s" % (url_return ,"?error=Unknown_error"))
diff --git a/addons/google_account/google_account.py b/addons/google_account/google_account.py
index 4968925bd29..51c7e2c5bca 100644
--- a/addons/google_account/google_account.py
+++ b/addons/google_account/google_account.py
@@ -129,8 +129,7 @@ class google_service(osv.osv_memory):
return res
def _do_request(self, cr, uid, uri, params={}, headers={}, type='POST', preuri="https://www.googleapis.com", context=None):
- if context is None:
- context = {}
+ context = dict(context or {})
""" Return a tuple ('HTTP_CODE', 'HTTP_RESPONSE') """
_logger.debug("Uri: %s - Type : %s - Headers: %s - Params : %s !" % (uri, type, headers, werkzeug.url_encode(params) if type == 'GET' else params))
diff --git a/addons/google_calendar/google_calendar.py b/addons/google_calendar/google_calendar.py
index dd1d45c05fe..7abef0faf8c 100644
--- a/addons/google_calendar/google_calendar.py
+++ b/addons/google_calendar/google_calendar.py
@@ -630,8 +630,7 @@ class google_calendar(osv.AbstractModel):
return new_ids
def update_events(self, cr, uid, lastSync=False, context=None):
- if context is None:
- context = {}
+ context = dict(context or {})
calendar_event = self.pool['calendar.event']
user_obj = self.pool['res.users']
@@ -937,7 +936,6 @@ class calendar_event(osv.Model):
def copy(self, cr, uid, id, default=None, context=None):
default = default or {}
- default['attendee_ids'] = False
if default.get('write_type', False):
del default['write_type']
elif default.get('recurrent_id', False):
diff --git a/addons/google_drive/google_drive.py b/addons/google_drive/google_drive.py
index 779b0231540..036b815648b 100644
--- a/addons/google_drive/google_drive.py
+++ b/addons/google_drive/google_drive.py
@@ -40,7 +40,7 @@ class config(osv.Model):
config = self.browse(cr, SUPERUSER_ID, config_id, context=context)
model = config.model_id
filter_name = config.filter_id and config.filter_id.name or False
- record = self.pool.get(model.model).read(cr, uid, res_id, [], context=context)
+ record = self.pool.get(model.model).read(cr, uid, [res_id], context=context)[0]
record.update({'model': model.name, 'filter': filter_name})
name_gdocs = config.name_template
try:
diff --git a/addons/google_drive/static/src/xml/gdocs.xml b/addons/google_drive/static/src/xml/gdocs.xml
deleted file mode 100644
index 0c8d9c3b01f..00000000000
--- a/addons/google_drive/static/src/xml/gdocs.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
Add Google Doc...
-
-
diff --git a/addons/google_spreadsheet/google_spreadsheet.py b/addons/google_spreadsheet/google_spreadsheet.py
index 4c24a46a404..df63ac5f632 100644
--- a/addons/google_spreadsheet/google_spreadsheet.py
+++ b/addons/google_spreadsheet/google_spreadsheet.py
@@ -58,7 +58,7 @@ class config(osv.osv):
formula = '=oe_browse("%s";"%s";"%s")' % (model, fields, domain)
url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
dbname = cr.dbname
- user = self.pool['res.users'].read(cr, uid, uid, ['login', 'password'], context=context)
+ user = self.pool['res.users'].read(cr, uid, [uid], ['login', 'password'], context=context)[0]
username = user['login']
password = user['password']
if not password:
diff --git a/addons/hr/hr.py b/addons/hr/hr.py
index a0016de87a2..0d4ab54a98c 100644
--- a/addons/hr/hr.py
+++ b/addons/hr/hr.py
@@ -110,8 +110,10 @@ class hr_job(osv.Model):
'hr.employee': (_get_job_position, ['job_id'], 10),
}, type='integer',
multi='_get_nbr_employees'),
- 'no_of_recruitment': fields.integer('Expected New Employees', help='Number of new employees you expect to recruit.'),
- 'no_of_hired_employee': fields.integer('Hired Employees', help='Number of hired employees for this job position during recruitment phase.'),
+ 'no_of_recruitment': fields.integer('Expected New Employees', copy=False,
+ help='Number of new employees you expect to recruit.'),
+ 'no_of_hired_employee': fields.integer('Hired Employees', copy=False,
+ help='Number of hired employees for this job position during recruitment phase.'),
'employee_ids': fields.one2many('hr.employee', 'job_id', 'Employees', groups='base.group_user'),
'description': fields.text('Job Description'),
'requirements': fields.text('Requirements'),
@@ -119,7 +121,7 @@ class hr_job(osv.Model):
'company_id': fields.many2one('res.company', 'Company'),
'state': fields.selection([('open', 'Recruitment Closed'), ('recruit', 'Recruitment in Progress')],
string='Status', readonly=True, required=True,
- track_visibility='always',
+ track_visibility='always', copy=False,
help="By default 'Closed', set it to 'In Recruitment' if recruitment process is going on for this job position."),
'write_date': fields.datetime('Update Date', readonly=True),
}
@@ -151,12 +153,7 @@ class hr_job(osv.Model):
def copy(self, cr, uid, id, default=None, context=None):
if default is None:
default = {}
- default.update({
- 'employee_ids': [],
- 'no_of_recruitment': 0,
- 'no_of_hired_employee': 0,
- })
- if 'name' in default:
+ if 'name' not in default:
job = self.browse(cr, uid, id, context=context)
default['name'] = _("%s (copy)") % (job.name)
return super(hr_job, self).copy(cr, uid, id, default=default, context=context)
@@ -248,13 +245,7 @@ class hr_employee(osv.osv):
'image': _get_default_image,
'color': 0,
}
-
- def copy_data(self, cr, uid, ids, default=None, context=None):
- if default is None:
- default = {}
- default.update({'child_ids': False})
- return super(hr_employee, self).copy_data(cr, uid, ids, default, context=context)
-
+
def _broadcast_welcome(self, cr, uid, employee_id, context=None):
""" Broadcast the welcome message to all users in the employee company. """
employee = self.browse(cr, uid, employee_id, context=context)
@@ -286,8 +277,7 @@ class hr_employee(osv.osv):
return True
def create(self, cr, uid, data, context=None):
- if context is None:
- context = {}
+ context = dict(context or {})
if context.get("mail_broadcast"):
context['mail_create_nolog'] = True
@@ -429,23 +419,10 @@ class hr_department(osv.osv):
res.append((record['id'], name))
return res
- def copy_data(self, cr, uid, ids, default=None, context=None):
- if default is None:
- default = {}
- default['member_ids'] = []
- return super(hr_department, self).copy_data(cr, uid, ids, default, context=context)
-
class res_users(osv.osv):
_name = 'res.users'
_inherit = 'res.users'
-
- def copy_data(self, cr, uid, ids, default=None, context=None):
- if default is None:
- default = {}
- default.update({'employee_ids': False})
- return super(res_users, self).copy_data(cr, uid, ids, default, context=context)
-
_columns = {
'employee_ids': fields.one2many('hr.employee', 'user_id', 'Related employees'),
}
diff --git a/addons/hr/images/photo.png b/addons/hr/images/photo.png
deleted file mode 100644
index 1edfe4754fc..00000000000
Binary files a/addons/hr/images/photo.png and /dev/null differ
diff --git a/addons/hr/res_users.py b/addons/hr/res_users.py
index d5bebe05446..78c0b841255 100644
--- a/addons/hr/res_users.py
+++ b/addons/hr/res_users.py
@@ -46,6 +46,7 @@ class res_users(osv.Model):
def _message_post_get_eid(self, cr, uid, thread_id, context=None):
assert thread_id, "res.users does not support posting global messages"
if context and 'thread_model' in context:
+ context = dict(context or {})
context['thread_model'] = 'hr.employee'
if isinstance(thread_id, (list, tuple)):
thread_id = thread_id[0]
diff --git a/addons/hr_evaluation/hr_evaluation.py b/addons/hr_evaluation/hr_evaluation.py
index 8c054f05726..068af5be0ee 100644
--- a/addons/hr_evaluation/hr_evaluation.py
+++ b/addons/hr_evaluation/hr_evaluation.py
@@ -35,7 +35,7 @@ class hr_evaluation_plan(osv.Model):
_columns = {
'name': fields.char("Appraisal Plan", required=True),
'company_id': fields.many2one('res.company', 'Company', required=True),
- 'phase_ids': fields.one2many('hr_evaluation.plan.phase', 'plan_id', 'Appraisal Phases'),
+ 'phase_ids': fields.one2many('hr_evaluation.plan.phase', 'plan_id', 'Appraisal Phases', copy=True),
'month_first': fields.integer('First Appraisal in (months)', help="This number of months will be used to schedule the first evaluation date of the employee when selecting an evaluation plan. "),
'month_next': fields.integer('Periodicity of Appraisal (months)', help="The number of month that depicts the delay between each evaluation of this plan (after the first one)."),
'active': fields.boolean('Active')
@@ -157,7 +157,7 @@ class hr_evaluation(osv.Model):
('wait', 'Plan In Progress'),
('progress', 'Waiting Appreciation'),
('done', 'Done'),
- ], 'Status', required=True, readonly=True),
+ ], 'Status', required=True, readonly=True, copy=False),
'date_close': fields.date('Ending Date', select=True),
}
_defaults = {
@@ -254,15 +254,6 @@ class hr_evaluation(osv.Model):
self.write(cr, uid, ids, {'state': 'draft'}, context=context)
return True
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- if context is None:
- context = {}
- default = default.copy()
- default['survey_request_ids'] = []
- return super(hr_evaluation, self).copy(cr, uid, id, default, context=context)
-
def write(self, cr, uid, ids, vals, context=None):
if vals.get('employee_id'):
employee_id = self.pool.get('hr.employee').browse(cr, uid, vals.get('employee_id'), context=context)
@@ -292,7 +283,7 @@ class hr_evaluation_interview(osv.Model):
('waiting_answer', "In progress"),
('done', "Done"),
('cancel', "Cancelled")],
- string="State", required=True),
+ string="State", required=True, copy=False),
'survey_id': fields.related('phase_id', 'survey_id', string="Appraisal Form", type="many2one", relation="survey.survey"),
'deadline': fields.related('request_id', 'deadline', type="datetime", string="Deadline"),
}
@@ -363,7 +354,7 @@ class hr_evaluation_interview(osv.Model):
def action_print_survey(self, cr, uid, ids, context=None):
""" If response is available then print this response otherwise print survey form (print template of the survey) """
- context = context if context else {}
+ context = dict(context or {})
interview = self.browse(cr, uid, ids, context=context)[0]
survey_obj = self.pool.get('survey.survey')
response_obj = self.pool.get('survey.user_input')
@@ -372,7 +363,7 @@ class hr_evaluation_interview(osv.Model):
return survey_obj.action_print_survey(cr, uid, [interview.survey_id.id], context=context)
def action_start_survey(self, cr, uid, ids, context=None):
- context = context if context else {}
+ context = dict(context or {})
interview = self.browse(cr, uid, ids, context=context)[0]
survey_obj = self.pool.get('survey.survey')
response_obj = self.pool.get('survey.user_input')
diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py
index d134c0d5102..eb8a09678e7 100644
--- a/addons/hr_expense/hr_expense.py
+++ b/addons/hr_expense/hr_expense.py
@@ -71,11 +71,15 @@ class hr_expense_expense(osv.osv):
'journal_id': fields.many2one('account.journal', 'Force Journal', help = "The journal used when the expense is done."),
'employee_id': fields.many2one('hr.employee', "Employee", required=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'user_id': fields.many2one('res.users', 'User', required=True),
- 'date_confirm': fields.date('Confirmation Date', select=True, help="Date of the confirmation of the sheet expense. It's filled when the button Confirm is pressed."),
- 'date_valid': fields.date('Validation Date', select=True, help="Date of the acceptation of the sheet expense. It's filled when the button Accept is pressed."),
- 'user_valid': fields.many2one('res.users', 'Validation By', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
- 'account_move_id': fields.many2one('account.move', 'Ledger Posting'),
- 'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines', readonly=True, states={'draft':[('readonly',False)]} ),
+ 'date_confirm': fields.date('Confirmation Date', select=True, copy=False,
+ help="Date of the confirmation of the sheet expense. It's filled when the button Confirm is pressed."),
+ 'date_valid': fields.date('Validation Date', select=True, copy=False,
+ help="Date of the acceptation of the sheet expense. It's filled when the button Accept is pressed."),
+ 'user_valid': fields.many2one('res.users', 'Validation By', readonly=True, copy=False,
+ states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
+ 'account_move_id': fields.many2one('account.move', 'Ledger Posting', copy=False),
+ 'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines', copy=True,
+ readonly=True, states={'draft':[('readonly',False)]} ),
'note': fields.text('Note'),
'amount': fields.function(_amount, string='Total Amount', digits_compute=dp.get_precision('Account'),
store={
@@ -92,7 +96,7 @@ class hr_expense_expense(osv.osv):
('done', 'Waiting Payment'),
('paid', 'Paid'),
],
- 'Status', readonly=True, track_visibility='onchange',
+ 'Status', readonly=True, track_visibility='onchange', copy=False,
help='When the expense request is created the status is \'Draft\'.\n It is confirmed by the user and request is sent to admin, the status is \'Waiting Confirmation\'.\
\nIf the admin accepts it, the status is \'Accepted\'.\n If the accounting entries are made for the expense request, the status is \'Waiting Payment\'.'),
@@ -106,16 +110,6 @@ class hr_expense_expense(osv.osv):
'currency_id': _get_currency,
}
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default.update(
- account_move_id=False,
- date_confirm=False,
- date_valid=False,
- user_valid=False)
- return super(hr_expense_expense, self).copy(cr, uid, id, default=default, context=context)
-
def unlink(self, cr, uid, ids, context=None):
for rec in self.browse(cr, uid, ids, context=context):
if rec.state != 'draft':
@@ -209,9 +203,7 @@ class hr_expense_expense(osv.osv):
c: account_move_lines potentially modified
'''
cur_obj = self.pool.get('res.currency')
- if context is None:
- context={}
- context.update({'date': exp.date_confirm or time.strftime('%Y-%m-%d')})
+ context = dict(context or {}, date=exp.date_confirm or time.strftime('%Y-%m-%d'))
total = 0.0
total_currency = 0.0
for i in account_move_lines:
diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py
index d38cb705339..21cbe50dee2 100644
--- a/addons/hr_holidays/hr_holidays.py
+++ b/addons/hr_holidays/hr_holidays.py
@@ -148,12 +148,17 @@ class hr_holidays(osv.osv):
result[holiday.id] = True
return result
- def _check_date(self, cr, uid, ids):
- for holiday in self.browse(cr, uid, ids):
- holiday_ids = self.search(cr, uid, [('date_from', '<=', holiday.date_to), ('date_to', '>=', holiday.date_from),
- ('employee_id', '=', holiday.employee_id.id), ('id', '<>', holiday.id),
- ('state', 'not in', ['cancel', 'refuse'])])
- if holiday_ids:
+ def _check_date(self, cr, uid, ids, context=None):
+ for holiday in self.browse(cr, uid, ids, context=context):
+ domain = [
+ ('date_from', '<=', holiday.date_to),
+ ('date_to', '>=', holiday.date_from),
+ ('employee_id', '=', holiday.employee_id.id),
+ ('id', '!=', holiday.id),
+ ('state', 'not in', ['cancel', 'refuse']),
+ ]
+ nholidays = self.search_count(cr, uid, domain, context=context)
+ if nholidays:
return False
return True
@@ -162,19 +167,20 @@ class hr_holidays(osv.osv):
_columns = {
'name': fields.char('Description', size=64),
'state': fields.selection([('draft', 'To Submit'), ('cancel', 'Cancelled'),('confirm', 'To Approve'), ('refuse', 'Refused'), ('validate1', 'Second Approval'), ('validate', 'Approved')],
- 'Status', readonly=True, track_visibility='onchange',
+ 'Status', readonly=True, track_visibility='onchange', copy=False,
help='The status is set to \'To Submit\', when a holiday request is created.\
\nThe status is \'To Approve\', when holiday request is confirmed by user.\
\nThe status is \'Refused\', when holiday request is refused by manager.\
\nThe status is \'Approved\', when holiday request is approved by manager.'),
'user_id':fields.related('employee_id', 'user_id', type='many2one', relation='res.users', string='User', store=True),
- 'date_from': fields.datetime('Start Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, select=True),
- 'date_to': fields.datetime('End Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
+ 'date_from': fields.datetime('Start Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, select=True, copy=False),
+ 'date_to': fields.datetime('End Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, copy=False),
'holiday_status_id': fields.many2one("hr.holidays.status", "Leave Type", required=True,readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'employee_id': fields.many2one('hr.employee', "Employee", select=True, invisible=False, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
- 'manager_id': fields.many2one('hr.employee', 'First Approval', invisible=False, readonly=True, help='This area is automatically filled by the user who validate the leave'),
+ 'manager_id': fields.many2one('hr.employee', 'First Approval', invisible=False, readonly=True, copy=False,
+ help='This area is automatically filled by the user who validate the leave'),
'notes': fields.text('Reasons',readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
- 'number_of_days_temp': fields.float('Allocation', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
+ 'number_of_days_temp': fields.float('Allocation', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, copy=False),
'number_of_days': fields.function(_compute_number_of_days, string='Number of Days', store=True),
'meeting_id': fields.many2one('calendar.event', 'Meeting'),
'type': fields.selection([('remove','Leave Request'),('add','Allocation Request')], 'Request Type', required=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, help="Choose 'Leave Request' if someone wants to take an off-day. \nChoose 'Allocation Request' if you want to increase the number of leaves available for someone", select=True),
@@ -183,7 +189,8 @@ class hr_holidays(osv.osv):
'department_id':fields.related('employee_id', 'department_id', string='Department', type='many2one', relation='hr.department', readonly=True, store=True),
'category_id': fields.many2one('hr.employee.category', "Employee Tag", help='Category of Employee', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'holiday_type': fields.selection([('employee','By Employee'),('category','By Employee Tag')], 'Allocation Mode', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, help='By Employee: Allocation/Request for individual Employee, By Employee Tag: Allocation/Request for group of employees in category', required=True),
- 'manager_id2': fields.many2one('hr.employee', 'Second Approval', readonly=True, help='This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation)'),
+ 'manager_id2': fields.many2one('hr.employee', 'Second Approval', readonly=True, copy=False,
+ help='This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation)'),
'double_validation': fields.related('holiday_status_id', 'double_validation', type='boolean', relation='hr.holidays.status', string='Apply Double Validation'),
'can_reset': fields.function(
_get_can_reset,
@@ -207,16 +214,6 @@ class hr_holidays(osv.osv):
('date_check2', "CHECK ( (type='add') OR (date_from <= date_to))", "The start date must be anterior to the end date."),
('date_check', "CHECK ( number_of_days_temp >= 0 )", "The number of days must be greater than 0."),
]
-
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- if context is None:
- context = {}
- default = default.copy()
- default['date_from'] = False
- default['date_to'] = False
- return super(hr_holidays, self).copy(cr, uid, id, default, context=context)
def _create_resource_leave(self, cr, uid, leaves, context=None):
'''This method will create entry in resource calendar leave object at the time of holidays validated '''
@@ -404,9 +401,8 @@ class hr_holidays(osv.osv):
leave_ids.append(self.create(cr, uid, vals, context=None))
for leave_id in leave_ids:
# TODO is it necessary to interleave the calls?
- self.signal_confirm(cr, uid, [leave_id])
- self.signal_validate(cr, uid, [leave_id])
- self.signal_second_validate(cr, uid, [leave_id])
+ for sig in ('confirm', 'validate', 'second_validate'):
+ self.signal_workflow(cr, uid, [leave_id], sig)
return True
def holidays_confirm(self, cr, uid, ids, context=None):
@@ -432,10 +428,10 @@ class hr_holidays(osv.osv):
for record in self.browse(cr, uid, ids):
# Delete the meeting
if record.meeting_id:
- meeting_obj.unlink(cr, uid, [record.meeting_id.id])
+ record.meeting_id.unlink()
# If a category that created several holidays, cancel all related
- self.signal_refuse(cr, uid, map(attrgetter('id'), record.linked_request_ids or []))
+ self.signal_workflow(cr, uid, map(attrgetter('id'), record.linked_request_ids or []), 'refuse')
self._remove_resource_leave(cr, uid, ids, context=context)
return True
@@ -507,9 +503,8 @@ class hr_employee(osv.osv):
leave_id = holiday_obj.create(cr, uid, {'name': _('Leave Request for %s') % employee.name, 'employee_id': employee.id, 'holiday_status_id': status_id, 'type': 'remove', 'holiday_type': 'employee', 'number_of_days_temp': abs(diff)}, context=context)
else:
return False
- holiday_obj.signal_confirm(cr, uid, [leave_id])
- holiday_obj.signal_validate(cr, uid, [leave_id])
- holiday_obj.signal_second_validate(cr, uid, [leave_id])
+ for sig in ('confirm', 'validate', 'second_validate'):
+ holiday_obj.signal_workflow(cr, uid, [leave_id], sig)
return True
def _get_remaining_days(self, cr, uid, ids, name, args, context=None):
diff --git a/addons/hr_holidays/report/holidays_summary_report.py b/addons/hr_holidays/report/holidays_summary_report.py
index adf09a63c85..a5889360774 100644
--- a/addons/hr_holidays/report/holidays_summary_report.py
+++ b/addons/hr_holidays/report/holidays_summary_report.py
@@ -50,8 +50,8 @@ def emp_create_xml(self, cr, uid, dept, holiday_type, row_id, empid, name, som,
if dept==0:
count=0
registry = openerp.registry(cr.dbname)
- p_id = registry['hr.holidays'].search(cr, uid, [('employee_id','in',[empid,False]), ('type', '=', 'remove')])
- ids_date = registry['hr.holidays'].read(cr, uid, p_id, ['date_from','date_to','holiday_status_id','state'])
+ holidays_ids = registry['hr.holidays'].search(cr, uid, [('employee_id','in',[empid,False]), ('type', '=', 'remove')])
+ ids_date = registry['hr.holidays'].read(cr, uid, holidays_ids, ['date_from','date_to','holiday_status_id','state'])
for index in range(1,61):
diff=index-1
@@ -213,18 +213,14 @@ class report_custom(report_rml):
emp_xml=''
row_id=1
- if data['model']=='hr.employee':
- for id in data['form']['emp']:
- items = obj_emp.read(cr, uid, id, ['id','name'])
-
- emp_xml += emp_create_xml(self, cr, uid, 0, holiday_type, row_id, items['id'], items['name'], som, eom)
- row_id = row_id +1
+ if data['model'] == 'hr.employee':
+ for items in obj_emp.read(cr, uid, data['form']['emp'], ['id', 'name']):
+ emp_xml += emp_create_xml(self, cr, uid, 0, holiday_type, row_id, items['id'], items['name'], som, eom)
+ row_id = row_id +1
elif data['model']=='ir.ui.menu':
- for id in data['form']['depts']:
- dept = obj_dept.browse(cr, uid, id, context=context)
- cr.execute("""SELECT id FROM hr_employee \
- WHERE department_id = %s""", (id,))
+ for dept in obj_dept.browse(cr, uid, data['form']['depts'], context=context):
+ cr.execute("SELECT id FROM hr_employee WHERE department_id = %s", (dept.id,))
emp_ids = [x[0] for x in cr.fetchall()]
if emp_ids==[]:
continue
diff --git a/addons/hr_holidays/test/test_hr_holiday.yml b/addons/hr_holidays/test/test_hr_holiday.yml
index 7bedd3302e8..1c00692c1f7 100644
--- a/addons/hr_holidays/test/test_hr_holiday.yml
+++ b/addons/hr_holidays/test/test_hr_holiday.yml
@@ -19,7 +19,7 @@
-
!python {model: hr.holidays}: |
self.holidays_reset(cr, uid, [ref('hr_holidays_employee1_cl')])
- self.signal_confirm(cr, uid, [ref('hr_holidays_employee1_cl')])
+ self.signal_workflow(cr, uid, [ref('hr_holidays_employee1_cl')], 'confirm')
-
I validate the holiday request by clicking on "To Approve" button.
-
diff --git a/addons/hr_holidays/tests/test_holidays_flow.py b/addons/hr_holidays/tests/test_holidays_flow.py
index ce7da7647d0..96ab20f6ae5 100644
--- a/addons/hr_holidays/tests/test_holidays_flow.py
+++ b/addons/hr_holidays/tests/test_holidays_flow.py
@@ -27,10 +27,9 @@ from openerp.exceptions import AccessError
from openerp.osv.orm import except_orm
from openerp.tools import mute_logger
-
class TestHolidaysFlow(TestHrHolidaysBase):
- @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
+ @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
def test_00_leave_request_flow(self):
""" Testing leave request flow """
cr, uid = self.cr, self.uid
@@ -82,6 +81,8 @@ class TestHolidaysFlow(TestHrHolidaysBase):
'date_to': datetime.today(),
'number_of_days_temp': 1,
})
+ ids = self.hr_holidays.search(cr, uid, [('name', '=', 'Hol10')])
+ self.hr_holidays.unlink(cr, uid, ids)
# Employee creates a leave request in a no-limit category
hol1_id = self.hr_holidays.create(cr, self.user_employee_id, {
@@ -96,12 +97,12 @@ class TestHolidaysFlow(TestHrHolidaysBase):
self.assertEqual(hol1.state, 'confirm', 'hr_holidays: newly created leave request should be in confirm state')
# Employee validates its leave request -> should not work
- self.hr_holidays.signal_validate(cr, self.user_employee_id, [hol1_id])
+ self.hr_holidays.signal_workflow(cr, self.user_employee_id, [hol1_id], 'validate')
hol1.refresh()
self.assertEqual(hol1.state, 'confirm', 'hr_holidays: employee should not be able to validate its own leave request')
# HrUser validates the employee leave request
- self.hr_holidays.signal_validate(cr, self.user_hrmanager_id, [hol1_id])
+ self.hr_holidays.signal_workflow(cr, self.user_hrmanager_id, [hol1_id], 'validate')
hol1.refresh()
self.assertEqual(hol1.state, 'validate', 'hr_holidays: validates leave request should be in validate state')
@@ -143,8 +144,8 @@ class TestHolidaysFlow(TestHrHolidaysBase):
'number_of_days_temp': 2,
})
# HrUser validates the allocation request
- self.hr_holidays.signal_validate(cr, self.user_hruser_id, [aloc1_id])
- self.hr_holidays.signal_second_validate(cr, self.user_hruser_id, [aloc1_id])
+ self.hr_holidays.signal_workflow(cr, self.user_hruser_id, [aloc1_id], 'validate')
+ self.hr_holidays.signal_workflow(cr, self.user_hruser_id, [aloc1_id], 'second_validate')
# Checks Employee has effectively some days left
hol_status_2 = self.hr_holidays_status.browse(cr, self.user_employee_id, self.holidays_status_2)
_check_holidays_status(hol_status_2, 2.0, 0.0, 2.0, 2.0)
@@ -164,13 +165,13 @@ class TestHolidaysFlow(TestHrHolidaysBase):
_check_holidays_status(hol_status_2, 2.0, 0.0, 2.0, 1.0)
# HrUser validates the first step
- self.hr_holidays.signal_validate(cr, self.user_hruser_id, [hol2_id])
+ self.hr_holidays.signal_workflow(cr, self.user_hruser_id, [hol2_id], 'validate')
hol2.refresh()
self.assertEqual(hol2.state, 'validate1',
'hr_holidays: first validation should lead to validate1 state')
# HrUser validates the second step
- self.hr_holidays.signal_second_validate(cr, self.user_hruser_id, [hol2_id])
+ self.hr_holidays.signal_workflow(cr, self.user_hruser_id, [hol2_id], 'second_validate')
hol2.refresh()
self.assertEqual(hol2.state, 'validate',
'hr_holidays: second validation should lead to validate state')
@@ -179,7 +180,7 @@ class TestHolidaysFlow(TestHrHolidaysBase):
_check_holidays_status(hol_status_2, 2.0, 1.0, 1.0, 1.0)
# HrManager finds an error: he refuses the leave request
- self.hr_holidays.signal_refuse(cr, self.user_hrmanager_id, [hol2_id])
+ self.hr_holidays.signal_workflow(cr, self.user_hrmanager_id, [hol2_id], 'refuse')
hol2.refresh()
self.assertEqual(hol2.state, 'refuse',
'hr_holidays: refuse should lead to refuse state')
@@ -188,12 +189,12 @@ class TestHolidaysFlow(TestHrHolidaysBase):
_check_holidays_status(hol_status_2, 2.0, 0.0, 2.0, 2.0)
# Annoyed, HrUser tries to fix its error and tries to reset the leave request -> does not work, only HrManager
- self.hr_holidays.signal_reset(cr, self.user_hruser_id, [hol2_id])
+ self.hr_holidays.signal_workflow(cr, self.user_hruser_id, [hol2_id], 'reset')
self.assertEqual(hol2.state, 'refuse',
'hr_holidays: hr_user should not be able to reset a refused leave request')
# HrManager resets the request
- self.hr_holidays.signal_reset(cr, self.user_hrmanager_id, [hol2_id])
+ self.hr_holidays.signal_workflow(cr, self.user_hrmanager_id, [hol2_id], 'reset')
hol2.refresh()
self.assertEqual(hol2.state, 'draft',
'hr_holidays: resetting should lead to draft state')
@@ -205,4 +206,4 @@ class TestHolidaysFlow(TestHrHolidaysBase):
'number_of_days_temp': 4,
})
with self.assertRaises(except_orm):
- self.hr_holidays.signal_confirm(cr, self.user_hrmanager_id, [hol2_id])
+ self.hr_holidays.signal_workflow(cr, self.user_hrmanager_id, [hol2_id], 'confirm')
diff --git a/addons/hr_holidays/wizard/hr_holidays_summary_department.py b/addons/hr_holidays/wizard/hr_holidays_summary_department.py
index 8a204389572..ff7bacc3e13 100644
--- a/addons/hr_holidays/wizard/hr_holidays_summary_department.py
+++ b/addons/hr_holidays/wizard/hr_holidays_summary_department.py
@@ -39,7 +39,7 @@ class hr_holidays_summary_dept(osv.osv_memory):
}
def print_report(self, cr, uid, ids, context=None):
- data = self.read(cr, uid, ids, [], context=context)[0]
+ data = self.read(cr, uid, ids, context=context)[0]
if not data['depts']:
raise osv.except_osv(_('Error!'), _('You have to select at least one Department. And try again.'))
datas = {
diff --git a/addons/hr_holidays/wizard/hr_holidays_summary_employees.py b/addons/hr_holidays/wizard/hr_holidays_summary_employees.py
index 1ec08eb2c86..324a7f94240 100644
--- a/addons/hr_holidays/wizard/hr_holidays_summary_employees.py
+++ b/addons/hr_holidays/wizard/hr_holidays_summary_employees.py
@@ -37,7 +37,7 @@ class hr_holidays_summary_employee(osv.osv_memory):
}
def print_report(self, cr, uid, ids, context=None):
- data = self.read(cr, uid, ids, [], context=context)[0]
+ data = self.read(cr, uid, ids, context=context)[0]
data['emp'] = context['active_ids']
datas = {
'ids': [],
diff --git a/addons/hr_payroll/hr_payroll.py b/addons/hr_payroll/hr_payroll.py
index 37b7e9eed3e..4dea994a130 100644
--- a/addons/hr_payroll/hr_payroll.py
+++ b/addons/hr_payroll/hr_payroll.py
@@ -26,8 +26,8 @@ from datetime import datetime
from datetime import timedelta
from dateutil import relativedelta
+from openerp import api, tools
from openerp.osv import fields, osv
-from openerp import tools
from openerp.tools.translate import _
import openerp.addons.decimal_precision as dp
@@ -46,10 +46,10 @@ class hr_payroll_structure(osv.osv):
_columns = {
'name':fields.char('Name', required=True),
'code':fields.char('Reference', size=64, required=True),
- 'company_id':fields.many2one('res.company', 'Company', required=True),
+ 'company_id':fields.many2one('res.company', 'Company', required=True, copy=False),
'note': fields.text('Description'),
'parent_id':fields.many2one('hr.payroll.structure', 'Parent'),
- 'children_ids':fields.one2many('hr.payroll.structure', 'parent_id', 'Children'),
+ 'children_ids':fields.one2many('hr.payroll.structure', 'parent_id', 'Children', copy=True),
'rule_ids':fields.many2many('hr.salary.rule', 'hr_structure_salary_rule_rel', 'struct_id', 'rule_id', 'Salary Rules'),
}
@@ -73,23 +73,11 @@ class hr_payroll_structure(osv.osv):
]
def copy(self, cr, uid, id, default=None, context=None):
- """
- Create a new record in hr_payroll_structure model from existing one
- @param cr: cursor to database
- @param user: id of current user
- @param id: list of record ids on which copy method executes
- @param default: dict type contains the values to be override during copy of object
- @param context: context arguments, like lang, time zone
-
- @return: returns a id of newly created record
- """
- if not default:
- default = {}
- default.update(
- code=_("%s (copy)") % (self.browse(cr, uid, id, context=context).code),
- company_id=self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id)
+ default = dict(default or {},
+ code=_("%s (copy)") % (self.browse(cr, uid, id, context=context).code))
return super(hr_payroll_structure, self).copy(cr, uid, id, default, context=context)
+ @api.cr_uid_ids_context
def get_all_rules(self, cr, uid, structure_ids, context=None):
"""
@param structure_ids: list of structure
@@ -101,6 +89,7 @@ class hr_payroll_structure(osv.osv):
all_rules += self.pool.get('hr.salary.rule')._recursive_search_of_rules(cr, uid, struct.rule_ids, context=context)
return all_rules
+ @api.cr_uid_ids_context
def _get_parent_structure(self, cr, uid, struct_ids, context=None):
if not struct_ids:
return []
@@ -138,6 +127,7 @@ class hr_contract(osv.osv):
'schedule_pay': 'monthly',
}
+ @api.cr_uid_ids_context
def get_all_structures(self, cr, uid, contract_ids, context=None):
"""
@param contract_ids: list of contracts
@@ -205,8 +195,14 @@ class one2many_mod2(fields.one2many):
for id in ids:
res[id] = []
ids2 = obj.pool[self._obj].search(cr, user, [(self._fields_id,'in',ids), ('appears_on_payslip', '=', True)], limit=self._limit)
- for r in obj.pool[self._obj]._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'):
- res[r[self._fields_id]].append( r['id'] )
+ for r in obj.pool[self._obj].read(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'):
+ key = r[self._fields_id]
+ if isinstance(key, tuple):
+ # Read return a tuple in the case where the field is a many2one
+ # but we want to get the id of this field.
+ key = key[0]
+
+ res[key].append( r['id'] )
return res
class hr_payslip_run(osv.osv):
@@ -219,7 +215,7 @@ class hr_payslip_run(osv.osv):
'state': fields.selection([
('draft', 'Draft'),
('close', 'Close'),
- ], 'Status', select=True, readonly=True),
+ ], 'Status', select=True, readonly=True, copy=False),
'date_start': fields.date('Date From', required=True, readonly=True, states={'draft': [('readonly', False)]}),
'date_end': fields.date('Date To', required=True, readonly=True, states={'draft': [('readonly', False)]}),
'credit_note': fields.boolean('Credit Note', readonly=True, states={'draft': [('readonly', False)]}, help="If its checked, indicates that all payslips generated from here are refund payslips."),
@@ -268,7 +264,7 @@ class hr_payslip(osv.osv):
_columns = {
'struct_id': fields.many2one('hr.payroll.structure', 'Structure', readonly=True, states={'draft': [('readonly', False)]}, help='Defines the rules that have to be applied to this payslip, accordingly to the contract chosen. If you let empty the field contract, this field isn\'t mandatory anymore and thus the rules applied will be all the rules set on the structure of all contracts of the employee valid for the chosen period'),
'name': fields.char('Payslip Name', required=False, readonly=True, states={'draft': [('readonly', False)]}),
- 'number': fields.char('Reference', required=False, readonly=True, states={'draft': [('readonly', False)]}),
+ 'number': fields.char('Reference', required=False, readonly=True, states={'draft': [('readonly', False)]}, copy=False),
'employee_id': fields.many2one('hr.employee', 'Employee', required=True, readonly=True, states={'draft': [('readonly', False)]}),
'date_from': fields.date('Date From', readonly=True, states={'draft': [('readonly', False)]}, required=True),
'date_to': fields.date('Date To', readonly=True, states={'draft': [('readonly', False)]}, required=True),
@@ -277,21 +273,21 @@ class hr_payslip(osv.osv):
('verify', 'Waiting'),
('done', 'Done'),
('cancel', 'Rejected'),
- ], 'Status', select=True, readonly=True,
+ ], 'Status', select=True, readonly=True, copy=False,
help='* When the payslip is created the status is \'Draft\'.\
\n* If the payslip is under verification, the status is \'Waiting\'. \
\n* If the payslip is confirmed then status is set to \'Done\'.\
\n* When user cancel payslip the status is \'Rejected\'.'),
'line_ids': one2many_mod2('hr.payslip.line', 'slip_id', 'Payslip Lines', readonly=True, states={'draft':[('readonly',False)]}),
- 'company_id': fields.many2one('res.company', 'Company', required=False, readonly=True, states={'draft': [('readonly', False)]}),
+ 'company_id': fields.many2one('res.company', 'Company', required=False, readonly=True, states={'draft': [('readonly', False)]}, copy=False),
'worked_days_line_ids': fields.one2many('hr.payslip.worked_days', 'payslip_id', 'Payslip Worked Days', required=False, readonly=True, states={'draft': [('readonly', False)]}),
'input_line_ids': fields.one2many('hr.payslip.input', 'payslip_id', 'Payslip Inputs', required=False, readonly=True, states={'draft': [('readonly', False)]}),
- 'paid': fields.boolean('Made Payment Order ? ', required=False, readonly=True, states={'draft': [('readonly', False)]}),
+ 'paid': fields.boolean('Made Payment Order ? ', required=False, readonly=True, states={'draft': [('readonly', False)]}, copy=False),
'note': fields.text('Internal Note', readonly=True, states={'draft':[('readonly',False)]}),
'contract_id': fields.many2one('hr.contract', 'Contract', required=False, readonly=True, states={'draft': [('readonly', False)]}),
'details_by_salary_rule_category': fields.function(_get_lines_salary_rule_category, method=True, type='one2many', relation='hr.payslip.line', string='Details by Salary Rule Category'),
'credit_note': fields.boolean('Credit Note', help="Indicates this payslip has a refund of another", readonly=True, states={'draft': [('readonly', False)]}),
- 'payslip_run_id': fields.many2one('hr.payslip.run', 'Payslip Batches', readonly=True, states={'draft': [('readonly', False)]}),
+ 'payslip_run_id': fields.many2one('hr.payslip.run', 'Payslip Batches', readonly=True, states={'draft': [('readonly', False)]}, copy=False),
'payslip_count': fields.function(_count_detail_payslip, type='integer', string="Payslip Computation Details"),
}
_defaults = {
@@ -312,19 +308,6 @@ class hr_payslip(osv.osv):
_constraints = [(_check_dates, "Payslip 'Date From' must be before 'Date To'.", ['date_from', 'date_to'])]
- def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
- default.update({
- 'line_ids': [],
- 'company_id': company_id,
- 'number': '',
- 'payslip_run_id': False,
- 'paid': False,
- })
- return super(hr_payslip, self).copy(cr, uid, id, default, context=context)
-
def cancel_sheet(self, cr, uid, ids, context=None):
return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
@@ -340,8 +323,8 @@ class hr_payslip(osv.osv):
for payslip in self.browse(cr, uid, ids, context=context):
id_copy = self.copy(cr, uid, payslip.id, {'credit_note': True, 'name': _('Refund: ')+payslip.name}, context=context)
self.compute_sheet(cr, uid, [id_copy], context=context)
- self.signal_hr_verify_sheet(cr, uid, [id_copy])
- self.signal_process_sheet(cr, uid, [id_copy])
+ self.signal_workflow(cr, uid, [id_copy], 'hr_verify_sheet')
+ self.signal_workflow(cr, uid, [id_copy], 'process_sheet')
form_id = mod_obj.get_object_reference(cr, uid, 'hr_payroll', 'view_hr_payslip_form')
form_res = form_id and form_id[1] or False
@@ -710,14 +693,12 @@ class hr_payslip(osv.osv):
def onchange_contract_id(self, cr, uid, ids, date_from, date_to, employee_id=False, contract_id=False, context=None):
#TODO it seems to be the mess in the onchanges, we should have onchange_employee => onchange_contract => doing all the things
- if context is None:
- context = {}
res = {'value':{
'line_ids': [],
'name': '',
}
}
- context.update({'contract': True})
+ context = dict(context or {}, contract=True)
if not contract_id:
res['value'].update({'struct_id': False})
return self.onchange_employee_id(cr, uid, ids, date_from=date_from, date_to=date_to, employee_id=employee_id, contract_id=contract_id, context=context)
@@ -792,10 +773,10 @@ class hr_salary_rule(osv.osv):
'amount_fix': fields.float('Fixed Amount', digits_compute=dp.get_precision('Payroll'),),
'amount_percentage': fields.float('Percentage (%)', digits_compute=dp.get_precision('Payroll Rate'), help='For example, enter 50.0 to apply a percentage of 50%'),
'amount_python_compute':fields.text('Python Code'),
- 'amount_percentage_base':fields.char('Percentage based on', required=False, readonly=False, help='result will be affected to a variable'),
- 'child_ids':fields.one2many('hr.salary.rule', 'parent_rule_id', 'Child Salary Rule'),
+ 'amount_percentage_base': fields.char('Percentage based on', required=False, readonly=False, help='result will be affected to a variable'),
+ 'child_ids':fields.one2many('hr.salary.rule', 'parent_rule_id', 'Child Salary Rule', copy=True),
'register_id':fields.many2one('hr.contribution.register', 'Contribution Register', help="Eventual third party involved in the salary payment of the employees."),
- 'input_ids': fields.one2many('hr.rule.input', 'input_id', 'Inputs'),
+ 'input_ids': fields.one2many('hr.rule.input', 'input_id', 'Inputs', copy=True),
'note':fields.text('Description'),
}
_defaults = {
@@ -842,6 +823,7 @@ result = rules.NET > categories.NET * 0.10''',
'quantity': '1.0',
}
+ @api.cr_uid_ids_context
def _recursive_search_of_rules(self, cr, uid, rule_ids, context=None):
"""
@param rule_ids: list of browse record
diff --git a/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.py b/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.py
index 20e3c0413aa..a1c5a78a934 100644
--- a/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.py
+++ b/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.py
@@ -42,7 +42,7 @@ class payslip_lines_contribution_register(osv.osv_memory):
datas = {
'ids': context.get('active_ids', []),
'model': 'hr.contribution.register',
- 'form': self.read(cr, uid, ids, [], context=context)[0]
+ 'form': self.read(cr, uid, ids, context=context)[0]
}
return self.pool['report'].get_action(
cr, uid, [], 'hr_payroll.report_contributionregister', data=datas, context=context
diff --git a/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py b/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py
index 3bf8b339300..3658cc835bc 100644
--- a/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py
+++ b/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py
@@ -44,7 +44,7 @@ class hr_payslip_employees(osv.osv_memory):
data = self.read(cr, uid, ids, context=context)[0]
run_data = {}
if context and context.get('active_id', False):
- run_data = run_pool.read(cr, uid, context['active_id'], ['date_start', 'date_end', 'credit_note'])
+ run_data = run_pool.read(cr, uid, [context['active_id']], ['date_start', 'date_end', 'credit_note'])[0]
from_date = run_data.get('date_start', False)
to_date = run_data.get('date_end', False)
credit_note = run_data.get('credit_note', False)
diff --git a/addons/hr_payroll_account/hr_payroll_account.py b/addons/hr_payroll_account/hr_payroll_account.py
index ad8527fce43..879614f9e3e 100644
--- a/addons/hr_payroll_account/hr_payroll_account.py
+++ b/addons/hr_payroll_account/hr_payroll_account.py
@@ -36,7 +36,7 @@ class hr_payslip(osv.osv):
_columns = {
'period_id': fields.many2one('account.period', 'Force Period',states={'draft': [('readonly', False)]}, readonly=True, domain=[('state','<>','done')], help="Keep empty to use the period of the validation(Payslip) date."),
'journal_id': fields.many2one('account.journal', 'Salary Journal',states={'draft': [('readonly', False)]}, readonly=True, required=True),
- 'move_id': fields.many2one('account.move', 'Accounting Entry', readonly=True),
+ 'move_id': fields.many2one('account.move', 'Accounting Entry', readonly=True, copy=False),
}
def _get_default_journal(self, cr, uid, context=None):
@@ -50,12 +50,6 @@ class hr_payslip(osv.osv):
'journal_id': _get_default_journal,
}
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default['move_id'] = False
- return super(hr_payslip, self).copy(cr, uid, id, default, context=context)
-
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
diff --git a/addons/hr_payroll_account/test/hr_payroll_account.yml b/addons/hr_payroll_account/test/hr_payroll_account.yml
index c637b5f319a..0dda2f6d04b 100644
--- a/addons/hr_payroll_account/test/hr_payroll_account.yml
+++ b/addons/hr_payroll_account/test/hr_payroll_account.yml
@@ -104,7 +104,7 @@
-
!python {model: hr.payslip}: |
self.cancel_sheet(cr, uid, [ref("hr_payslip_0")], None)
- self.signal_draft(cr, uid, [ref("hr_payslip_0")])
+ self.signal_workflow(cr, uid, [ref("hr_payslip_0")], 'draft')
-
Then I click on the "Confirm" button.
-
diff --git a/addons/hr_payroll_account/wizard/hr_payroll_payslips_by_employees.py b/addons/hr_payroll_account/wizard/hr_payroll_payslips_by_employees.py
index 053fab16e84..2f04bfc676c 100644
--- a/addons/hr_payroll_account/wizard/hr_payroll_payslips_by_employees.py
+++ b/addons/hr_payroll_account/wizard/hr_payroll_payslips_by_employees.py
@@ -29,11 +29,12 @@ class hr_payslip_employees(osv.osv_memory):
run_pool = self.pool.get('hr.payslip.run')
if context is None:
context = {}
- if context and context.get('active_id', False):
+ if context.get('active_id'):
run_data = run_pool.read(cr, uid, context['active_id'], ['journal_id'])
- journal_id = run_data.get('journal_id', False)
+ journal_id = run_data.get('journal_id')
journal_id = journal_id and journal_id[0] or False
- if journal_id: context.update({'journal_id': journal_id})
+ if journal_id:
+ context = dict(context, journal_id=journal_id)
return super(hr_payslip_employees, self).compute_sheet(cr, uid, ids, context=context)
diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py
index 832e2ad9e0e..7df4e73c85f 100644
--- a/addons/hr_recruitment/hr_recruitment.py
+++ b/addons/hr_recruitment/hr_recruitment.py
@@ -324,7 +324,7 @@ class hr_applicant(osv.Model):
return res
def action_start_survey(self, cr, uid, ids, context=None):
- context = context if context else {}
+ context = dict(context or {})
applicant = self.browse(cr, uid, ids, context=context)[0]
survey_obj = self.pool.get('survey.survey')
response_obj = self.pool.get('survey.user_input')
@@ -341,7 +341,7 @@ class hr_applicant(osv.Model):
def action_print_survey(self, cr, uid, ids, context=None):
""" If response is available then print this response otherwise print survey form (print template of the survey) """
- context = context if context else {}
+ context = dict(context or {})
applicant = self.browse(cr, uid, ids, context=context)[0]
survey_obj = self.pool.get('survey.survey')
response_obj = self.pool.get('survey.user_input')
@@ -397,8 +397,7 @@ class hr_applicant(osv.Model):
return super(hr_applicant, self).message_new(cr, uid, msg, custom_values=defaults, context=context)
def create(self, cr, uid, vals, context=None):
- if context is None:
- context = {}
+ context = dict(context or {})
context['mail_create_nolog'] = True
if vals.get('department_id') and not context.get('default_department_id'):
context['default_department_id'] = vals.get('department_id')
@@ -500,13 +499,14 @@ class hr_applicant(osv.Model):
raise osv.except_osv(_('Warning!'), _('You must define an Applied Job and a Contact Name for this applicant.'))
action_model, action_id = model_data.get_object_reference(cr, uid, 'hr', 'open_view_employee_list')
- dict_act_window = act_window.read(cr, uid, action_id, [])
+ dict_act_window = act_window.read(cr, uid, [action_id], [])[0]
if emp_id:
dict_act_window['res_id'] = emp_id
dict_act_window['view_mode'] = 'form,tree'
return dict_act_window
def get_empty_list_help(self, cr, uid, help, context=None):
+ context = dict(context or {})
context['empty_list_help_model'] = 'hr.job'
context['empty_list_help_id'] = context.get('default_job_id', None)
context['empty_list_help_document_name'] = _("job applicants")
diff --git a/addons/hr_recruitment/wizard/hr_recruitment_create_partner_job.py b/addons/hr_recruitment/wizard/hr_recruitment_create_partner_job.py
index d2e778c38a4..752b65845d7 100644
--- a/addons/hr_recruitment/wizard/hr_recruitment_create_partner_job.py
+++ b/addons/hr_recruitment/wizard/hr_recruitment_create_partner_job.py
@@ -46,7 +46,7 @@ class hr_recruitment_partner_create(osv.osv_memory):
if context is None:
context = {}
- data = self.read(cr, uid, ids, [], context=context)[0]
+ data = self.read(cr, uid, ids, context=context)[0]
result = mod_obj._get_id(cr, uid, 'base', 'view_res_partner_filter')
res = mod_obj.read(cr, uid, result, ['res_id'], context=context)
@@ -76,7 +76,7 @@ class hr_recruitment_partner_create(osv.osv_memory):
'view_id': False,
'type': 'ir.actions.act_window',
'search_view_id': res['res_id']
- }
+ }
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py
index a3d96b1ceba..ec8c89dbe64 100644
--- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py
+++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py
@@ -99,7 +99,7 @@ class account_analytic_account(osv.osv):
class account_analytic_line(osv.osv):
_inherit = 'account.analytic.line'
_columns = {
- 'invoice_id': fields.many2one('account.invoice', 'Invoice', ondelete="set null"),
+ 'invoice_id': fields.many2one('account.invoice', 'Invoice', ondelete="set null", copy=False),
'to_invoice': fields.many2one('hr_timesheet_invoice.factor', 'Invoiceable', help="It allows to set the discount while making invoice, keep empty if the activities should not be invoiced."),
}
@@ -141,14 +141,6 @@ class account_analytic_line(osv.osv):
_('You cannot modify an invoiced analytic line!'))
return True
- def copy(self, cursor, user, obj_id, default=None, context=None):
- if default is None:
- default = {}
- default = default.copy()
- default.update({'invoice_id': False})
- return super(account_analytic_line, self).copy(cursor, user, obj_id,
- default, context=context)
-
def _get_invoice_price(self, cr, uid, account, product_id, user_id, qty, context = {}):
pro_price_obj = self.pool.get('product.pricelist')
if account.pricelist_id:
@@ -291,6 +283,7 @@ class account_analytic_line(osv.osv):
curr_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '',note)))
invoice_line_obj.create(cr, uid, curr_line, context=context)
cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(ids)))
+ self.invalidate_cache(cr, uid, ['invoice_id'], ids, context=context)
invoice_obj.button_reset_taxes(cr, uid, [last_invoice], context)
return invoices
@@ -314,23 +307,13 @@ class hr_analytic_timesheet(osv.osv):
}
return res
- def copy(self, cursor, user, obj_id, default=None, context=None):
- if default is None:
- default = {}
- default = default.copy()
- default.update({'invoice_id': False})
- return super(hr_analytic_timesheet, self).copy(cursor, user, obj_id,
- default, context=context)
-
-
-
class account_invoice(osv.osv):
_inherit = "account.invoice"
- def _get_analytic_lines(self, cr, uid, id, context=None):
- iml = super(account_invoice, self)._get_analytic_lines(cr, uid, id, context=context)
+ def _get_analytic_lines(self, cr, uid, ids, context=None):
+ iml = super(account_invoice, self)._get_analytic_lines(cr, uid, ids, context=context)
- inv = self.browse(cr, uid, [id], context=context)[0]
+ inv = self.browse(cr, uid, ids, context=context)[0]
if inv.type == 'in_invoice':
obj_analytic_account = self.pool.get('account.analytic.account')
for il in iml:
diff --git a/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice.yml b/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice.yml
index 49243e501f2..b6b52544cbc 100644
--- a/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice.yml
+++ b/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice.yml
@@ -61,7 +61,7 @@
-
I set the account as property_account_income on the product and I set the tax on the product
-
- !record {model: product.product, id: product.product_product_consultant}:
+ !record {model: product.product, id: product.product_product_consultant, view: False}:
property_account_income: account_income_i000
uom_id: product.product_uom_hour
taxes_id: [tax10]
diff --git a/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice_no_prod_tax.yml b/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice_no_prod_tax.yml
index c3a5fae866c..c5b7c9b5be8 100644
--- a/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice_no_prod_tax.yml
+++ b/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice_no_prod_tax.yml
@@ -60,7 +60,7 @@
-
I set the account as property_account_income on the product and I set no tax on the product (so default tax from the account will be used)
-
- !record {model: product.product, id: product.product_product_consultant}:
+ !record {model: product.product, id: product.product_product_consultant, view: False}:
property_account_income: account_income_i000
uom_id: product.product_uom_hour
taxes_id: []
diff --git a/addons/hr_timesheet_invoice/wizard/hr_timesheet_analytic_profit.py b/addons/hr_timesheet_invoice/wizard/hr_timesheet_analytic_profit.py
index a78bc1c5bf5..03699eb35d6 100644
--- a/addons/hr_timesheet_invoice/wizard/hr_timesheet_analytic_profit.py
+++ b/addons/hr_timesheet_invoice/wizard/hr_timesheet_analytic_profit.py
@@ -48,7 +48,7 @@ class account_analytic_profit(osv.osv_memory):
def print_report(self, cr, uid, ids, context=None):
line_obj = self.pool.get('account.analytic.line')
data = {}
- data['form'] = self.read(cr, uid , ids, [], context=context)[0]
+ data['form'] = self.read(cr, uid , ids, context=context)[0]
ids_chk = line_obj.search(cr, uid, [
('date', '>=', data['form']['date_from']),
('date', '<=', data['form']['date_to']),
diff --git a/addons/hr_timesheet_invoice/wizard/hr_timesheet_final_invoice_create.py b/addons/hr_timesheet_invoice/wizard/hr_timesheet_final_invoice_create.py
index 8d5f0c4067c..1a2d77c21b1 100644
--- a/addons/hr_timesheet_invoice/wizard/hr_timesheet_final_invoice_create.py
+++ b/addons/hr_timesheet_invoice/wizard/hr_timesheet_final_invoice_create.py
@@ -43,7 +43,7 @@ class final_invoice_create(osv.osv_memory):
}
def do_create(self, cr, uid, ids, context=None):
- data = self.read(cr, uid, ids, [], context=context)[0]
+ data = self.read(cr, uid, ids, context=context)[0]
# hack for fixing small issue (context should not propagate implicitly between actions)
if 'default_type' in context:
del context['default_type']
@@ -53,7 +53,7 @@ class final_invoice_create(osv.osv_memory):
act_obj = self.pool.get('ir.actions.act_window')
mod_ids = mod_obj.search(cr, uid, [('name', '=', 'action_invoice_tree1')], context=context)[0]
res_id = mod_obj.read(cr, uid, mod_ids, ['res_id'], context=context)['res_id']
- act_win = act_obj.read(cr, uid, res_id, [], context=context)
+ act_win = act_obj.read(cr, uid, [res_id], context=context)[0]
act_win['domain'] = [('id','in',invs),('type','=','out_invoice')]
act_win['name'] = _('Invoices')
return act_win
diff --git a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py
index 874370fea22..344f219ca5f 100644
--- a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py
+++ b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py
@@ -56,14 +56,14 @@ class hr_timesheet_invoice_create(osv.osv_memory):
raise osv.except_osv(_('Warning!'), _("Invoice is already linked to some of the analytic line(s)!"))
def do_create(self, cr, uid, ids, context=None):
- data = self.read(cr, uid, ids, [], context=context)[0]
+ data = self.read(cr, uid, ids, context=context)[0]
# Create an invoice based on selected timesheet lines
invs = self.pool.get('account.analytic.line').invoice_cost_create(cr, uid, context['active_ids'], data, context=context)
mod_obj = self.pool.get('ir.model.data')
act_obj = self.pool.get('ir.actions.act_window')
- mod_ids = mod_obj.search(cr, uid, [('name', '=', 'action_invoice_tree1')], context=context)[0]
- res_id = mod_obj.read(cr, uid, mod_ids, ['res_id'], context=context)['res_id']
- act_win = act_obj.read(cr, uid, res_id, [], context=context)
+ mod_ids = mod_obj.search(cr, uid, [('name', '=', 'action_invoice_tree1')], context=context)
+ res_id = mod_obj.read(cr, uid, mod_ids, ['res_id'], context=context)[0]['res_id']
+ act_win = act_obj.read(cr, uid, [res_id], context=context)[0]
act_win['domain'] = [('id','in',invs),('type','=','out_invoice')]
act_win['name'] = _('Invoices')
return act_win
diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py
index 425f64062dc..a2845171212 100644
--- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py
+++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py
@@ -123,7 +123,7 @@ class hr_timesheet_sheet(osv.osv):
self.check_employee_attendance_state(cr, uid, sheet.id, context=context)
di = sheet.user_id.company_id.timesheet_max_difference
if (abs(sheet.total_difference) < di) or not di:
- self.signal_confirm(cr, uid, [sheet.id])
+ sheet.signal_workflow('confirm')
else:
raise osv.except_osv(_('Warning!'), _('Please verify that the total difference of the sheet is lower than %.2f.') %(di,))
return True
diff --git a/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml b/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml
index 614e8b19af5..c63133bd154 100644
--- a/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml
+++ b/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml
@@ -9,7 +9,7 @@
-
I assign this product(Service on Timesheet) and journal(Timesheet Journal) to employee "Gilles Gravie"
-
- !record {model: hr.employee, id: hr.employee_qdp}:
+ !record {model: hr.employee, id: hr.employee_qdp, view: False}:
product_id: product.product_product_consultant
journal_id: hr_timesheet.analytic_journal
-
@@ -27,7 +27,7 @@
!record {model: hr.attendance, id: hr_attendance_1}:
action: sign_in
employee_id: 'hr.employee_qdp'
- name: !eval datetime.now().strftime('%Y-%m-%d 09:%M:%S')
+ name: !eval datetime.now().strftime('%Y-%m-%d 09:12:37')
-
I test that Gilles in signed in
-
diff --git a/addons/im_livechat/im_livechat.py b/addons/im_livechat/im_livechat.py
index 23880a18986..ca7d1708bf5 100644
--- a/addons/im_livechat/im_livechat.py
+++ b/addons/im_livechat/im_livechat.py
@@ -87,10 +87,10 @@ class im_livechat_channel(osv.Model):
'are_you_inside': fields.function(_are_you_inside, type='boolean', string='Are you inside the matrix?', store=False),
'script_internal': fields.function(_script_internal, type='text', string='Script (internal)', store=False),
'script_external': fields.function(_script_external, type='text', string='Script (external)', store=False),
- 'web_page': fields.function(_web_page, type='url', string='Web Page', store=False, size="200"),
- 'button_text': fields.char(string="Text of the Button", size=200),
- 'input_placeholder': fields.char(string="Chat Input Placeholder", size=200),
- 'default_message': fields.char(string="Welcome Message", size=200, help="This is an automated 'welcome' message that your visitor will see when they initiate a new chat session."),
+ 'web_page': fields.function(_web_page, type='char', string='Web Page', store=False),
+ 'button_text': fields.char(string="Text of the Button"),
+ 'input_placeholder': fields.char(string="Chat Input Placeholder"),
+ 'default_message': fields.char(string="Welcome Message", help="This is an automated 'welcome' message that your visitor will see when they initiate a new chat session."),
# image: all image fields are base64 encoded and PIL-supported
'image': fields.binary("Photo",
help="This field holds the image used as photo for the group, limited to 1024x1024px."),
diff --git a/addons/im_livechat/views/im_livechat_view.xml b/addons/im_livechat/views/im_livechat_view.xml
index 8f14a81efc7..fa18f4cab16 100644
--- a/addons/im_livechat/views/im_livechat_view.xml
+++ b/addons/im_livechat/views/im_livechat_view.xml
@@ -29,7 +29,7 @@
-
+
@@ -112,7 +112,7 @@
or copy this url and send it by email to your customers or suppliers:
-
+
For website built with Odoo CMS, please install the website_livechat module. Then go to Settings > Website Settings and select the Live Chat Channel you want to add on your website.
diff --git a/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py b/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py
index 5734b7a17ab..0583d6787f0 100644
--- a/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py
+++ b/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py
@@ -196,6 +196,7 @@ class l10n_be_vat_declaration(osv.osv_memory):
data_of_file += '\n\t \n'
model_data_ids = mod_obj.search(cr, uid,[('model','=','ir.ui.view'),('name','=','view_vat_save')], context=context)
resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id']
+ context = dict(context or {})
context['file_save'] = data_of_file
return {
'name': _('Save XML For Vat declaration'),
diff --git a/addons/l10n_be/wizard/l10n_be_vat_intra.py b/addons/l10n_be/wizard/l10n_be_vat_intra.py
index 0629f9fa271..386aa0906fd 100644
--- a/addons/l10n_be/wizard/l10n_be_vat_intra.py
+++ b/addons/l10n_be/wizard/l10n_be_vat_intra.py
@@ -253,6 +253,7 @@ class partner_vat_intra(osv.osv_memory):
data_decl = '\n\t' % (xml_data)
data_file += data_head + data_decl + data_comp_period + data_clientinfo + '\n\t\t%(comments)s\n\t\n' % (xml_data)
+ context = dict(context or {})
context['file_save'] = data_file
model_data_ids = mod_obj.search(cursor, user,[('model','=','ir.ui.view'),('name','=','view_vat_intra_save')], context=context)
diff --git a/addons/l10n_be_invoice_bba/invoice.py b/addons/l10n_be_invoice_bba/invoice.py
index 337c5a3cdb4..257347071e2 100644
--- a/addons/l10n_be_invoice_bba/invoice.py
+++ b/addons/l10n_be_invoice_bba/invoice.py
@@ -21,6 +21,7 @@
##############################################################################
import re, time, random
+from openerp import api
from openerp.osv import fields, osv
from openerp.tools.translate import _
import logging
@@ -35,6 +36,7 @@ account.invoice object:
class account_invoice(osv.osv):
_inherit = 'account.invoice'
+ @api.cr_uid_context
def _get_reference_type(self, cursor, user, context=None):
"""Add BBA Structured Communication Type and change labels from 'reference' into 'communication' """
res = super(account_invoice, self)._get_reference_type(cursor, user,
diff --git a/addons/l10n_fr/l10n_fr.py b/addons/l10n_fr/l10n_fr.py
index 1173751431f..1f10e95b44e 100644
--- a/addons/l10n_fr/l10n_fr.py
+++ b/addons/l10n_fr/l10n_fr.py
@@ -27,7 +27,7 @@ class l10n_fr_report(osv.osv):
_columns = {
'code': fields.char('Code', size=64),
'name': fields.char('Name'),
- 'line_ids': fields.one2many('l10n.fr.line', 'report_id', 'Lines'),
+ 'line_ids': fields.one2many('l10n.fr.line', 'report_id', 'Lines', copy=True),
}
_sql_constraints = [
('code_uniq', 'unique (code)','The code report must be unique !')
diff --git a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py
index b18f2d1f1ae..0af3535bfd0 100644
--- a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py
+++ b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py
@@ -64,9 +64,9 @@ class payroll_advice(osv.osv):
('confirm', 'Confirmed'),
('cancel', 'Cancelled'),
], 'Status', select=True, readonly=True),
- 'number':fields.char('Reference', readonly=True),
- 'line_ids':fields.one2many('hr.payroll.advice.line', 'advice_id', 'Employee Salary', states={'draft': [('readonly', False)]}, readonly=True),
- 'chaque_nos':fields.char('Cheque Numbers'),
+ 'number': fields.char('Reference', readonly=True),
+ 'line_ids': fields.one2many('hr.payroll.advice.line', 'advice_id', 'Employee Salary', states={'draft': [('readonly', False)]}, readonly=True, copy=True),
+ 'chaque_nos': fields.char('Cheque Numbers'),
'neft': fields.boolean('NEFT Transaction', help="Check this box if your company use online transfer for salary"),
'company_id':fields.many2one('res.company', 'Company', required=True, readonly=True, states={'draft': [('readonly', False)]}),
'bank_id':fields.many2one('res.bank', 'Bank', readonly=True, states={'draft': [('readonly', False)]}, help="Select the Bank from which the salary is going to be paid"),
@@ -162,13 +162,10 @@ class hr_payslip_run(osv.osv):
_inherit = 'hr.payslip.run'
_description = 'Payslip Batches'
_columns = {
- 'available_advice': fields.boolean('Made Payment Advice?', help="If this box is checked which means that Payment Advice exists for current batch", readonly=False),
+ 'available_advice': fields.boolean('Made Payment Advice?',
+ help="If this box is checked which means that Payment Advice exists for current batch",
+ readonly=False, copy=False),
}
- def copy(self, cr, uid, id, default={}, context=None):
- if not default:
- default = {}
- default.update({'available_advice': False})
- return super(hr_payslip_run, self).copy(cr, uid, id, default, context=context)
def draft_payslip_run(self, cr, uid, ids, context=None):
res = super(hr_payslip_run, self).draft_payslip_run(cr, uid, ids, context=context)
@@ -195,8 +192,8 @@ class hr_payslip_run(osv.osv):
slip_ids = []
for slip_id in run.slip_ids:
# TODO is it necessary to interleave the calls ?
- payslip_pool.signal_hr_verify_sheet(cr, uid, [slip_id.id])
- payslip_pool.signal_process_sheet(cr, uid, [slip_id.id])
+ payslip_pool.signal_workflow(cr, uid, [slip_id.id], 'hr_verify_sheet')
+ payslip_pool.signal_workflow(cr, uid, [slip_id.id], 'process_sheet')
slip_ids.append(slip_id.id)
for slip in payslip_pool.browse(cr, uid, slip_ids, context=context):
@@ -252,16 +249,9 @@ class hr_payslip(osv.osv):
_inherit = 'hr.payslip'
_description = 'Pay Slips'
_columns = {
- 'advice_id': fields.many2one('hr.payroll.advice', 'Bank Advice')
+ 'advice_id': fields.many2one('hr.payroll.advice', 'Bank Advice', copy=False)
}
- def copy(self, cr, uid, id, default={}, context=None):
- if not default:
- default = {}
- default.update({'advice_id' : False})
- return super(hr_payslip, self).copy(cr, uid, id, default, context=context)
-
-
class res_company(osv.osv):
_inherit = 'res.company'
diff --git a/addons/l10n_ma/l10n_ma.py b/addons/l10n_ma/l10n_ma.py
index fe3fad6022f..d937d2daadc 100644
--- a/addons/l10n_ma/l10n_ma.py
+++ b/addons/l10n_ma/l10n_ma.py
@@ -28,7 +28,7 @@ class l10n_ma_report(osv.osv):
_columns = {
'code': fields.char('Code', size=64),
'name': fields.char('Name'),
- 'line_ids': fields.one2many('l10n.ma.line', 'report_id', 'Lines'),
+ 'line_ids': fields.one2many('l10n.ma.line', 'report_id', 'Lines', copy=True),
}
_sql_constraints = [
('code_uniq', 'unique (code)','The code report must be unique !')
diff --git a/addons/l10n_uk/i18n/l10n_chart_uk_minimal.pot b/addons/l10n_uk/i18n/l10n_chart_uk_minimal.pot
deleted file mode 100644
index 51fd5b8bd3f..00000000000
--- a/addons/l10n_uk/i18n/l10n_chart_uk_minimal.pot
+++ /dev/null
@@ -1,108 +0,0 @@
-# Translation of OpenERP Server.
-# This file contains the translation of the following modules:
-# * l10n_chart_uk_minimal
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: OpenERP Server 6.0dev\n"
-"Report-Msgid-Bugs-To: support@openerp.com\n"
-"POT-Creation-Date: 2010-10-18 17:46:36+0000\n"
-"PO-Revision-Date: 2010-10-18 17:46:36+0000\n"
-"Last-Translator: <>\n"
-"Language-Team: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: \n"
-
-#. module: l10n_chart_uk_minimal
-#: model:account.account.type,name:l10n_chart_uk_minimal.account_type_receivable
-msgid "Receivable"
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:account.account.type,name:l10n_chart_uk_minimal.acct_type_asset_view
-msgid "Asset View"
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:account.account.type,name:l10n_chart_uk_minimal.acct_type_expense_view
-msgid "Expense View"
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:ir.actions.todo,note:l10n_chart_uk_minimal.config_call_account_template_uk_minimal
-msgid "Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.\n"
-" This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template."
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: constraint:account.account.template:0
-msgid "Error ! You can not create recursive account templates."
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:account.account.type,name:l10n_chart_uk_minimal.acct_type_income_view
-msgid "Income View"
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:account.account.type,name:l10n_chart_uk_minimal.account_type_income
-msgid "Income"
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:account.account.type,name:l10n_chart_uk_minimal.account_type_tax
-msgid "Tax"
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:account.account.type,name:l10n_chart_uk_minimal.account_type_cash
-msgid "Cash"
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:account.account.type,name:l10n_chart_uk_minimal.account_type_liability
-msgid "Liability"
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:account.account.type,name:l10n_chart_uk_minimal.account_type_payable
-msgid "Payable"
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:ir.module.module,shortdesc:l10n_chart_uk_minimal.module_meta_information
-msgid "United Kingdom - minimal"
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:ir.module.module,description:l10n_chart_uk_minimal.module_meta_information
-msgid "This is the base module to manage the accounting chart for United Kingdom in OpenERP."
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:account.account.type,name:l10n_chart_uk_minimal.account_type_equity
-msgid "Equity"
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: constraint:account.tax.code.template:0
-msgid "Error ! You can not create recursive Tax Codes."
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:account.account.type,name:l10n_chart_uk_minimal.acct_type_liability_view
-msgid "Liability View"
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:account.account.type,name:l10n_chart_uk_minimal.account_type_expense
-msgid "Expense"
-msgstr ""
-
-#. module: l10n_chart_uk_minimal
-#: model:account.account.type,name:l10n_chart_uk_minimal.account_type_view
-msgid "View"
-msgstr ""
-
diff --git a/addons/lunch/lunch.py b/addons/lunch/lunch.py
index c976a6b4ed0..1d17b4468ac 100644
--- a/addons/lunch/lunch.py
+++ b/addons/lunch/lunch.py
@@ -299,7 +299,9 @@ class lunch_order(osv.Model):
_columns = {
'user_id': fields.many2one('res.users', 'User Name', required=True, readonly=True, states={'new':[('readonly', False)]}),
'date': fields.date('Date', required=True, readonly=True, states={'new':[('readonly', False)]}),
- 'order_line_ids': fields.one2many('lunch.order.line', 'order_id', 'Products', ondelete="cascade", readonly=True, states={'new':[('readonly', False)]}),
+ 'order_line_ids': fields.one2many('lunch.order.line', 'order_id', 'Products',
+ ondelete="cascade", readonly=True, states={'new':[('readonly', False)]},
+ copy=True),
'total': fields.function(_price_get, string="Total", store={
'lunch.order.line': (_fetch_orders_from_lines, ['product_id','order_id'], 20),
}),
@@ -307,7 +309,7 @@ class lunch_order(osv.Model):
('confirmed','Confirmed'), \
('cancelled','Cancelled'), \
('partially','Partially Confirmed')] \
- ,'Status', readonly=True, select=True),
+ ,'Status', readonly=True, select=True, copy=False),
'alerts': fields.function(_alerts_get, string="Alerts", type='text'),
}
@@ -336,8 +338,7 @@ class lunch_order_line(osv.Model):
"""
The order_line is ordered to the supplier but isn't received yet
"""
- for order_line in self.browse(cr, uid, ids, context=context):
- order_line.write({'state': 'ordered'}, context=context)
+ self.write(cr, uid, ids, {'state': 'ordered'}, context=context)
return self._update_order_lines(cr, uid, ids, context=context)
def confirm(self, cr, uid, ids, context=None):
@@ -356,7 +357,7 @@ class lunch_order_line(osv.Model):
'date': order_line.date,
}
cashmove_ref.create(cr, uid, values, context=context)
- order_line.write({'state': 'confirmed'}, context=context)
+ order_line.write({'state': 'confirmed'})
return self._update_order_lines(cr, uid, ids, context=context)
def _update_order_lines(self, cr, uid, ids, context=None):
@@ -384,8 +385,8 @@ class lunch_order_line(osv.Model):
cancel one or more order.line, update order status and unlink existing cashmoves
"""
cashmove_ref = self.pool.get('lunch.cashmove')
+ self.write(cr, uid, ids, {'state':'cancelled'}, context=context)
for order_line in self.browse(cr, uid, ids, context=context):
- order_line.write({'state':'cancelled'}, context=context)
cash_ids = [cash.id for cash in order_line.cashmove]
cashmove_ref.unlink(cr, uid, cash_ids, context=context)
return self._update_order_lines(cr, uid, ids, context=context)
diff --git a/addons/lunch/tests/test_lunch.py b/addons/lunch/tests/test_lunch.py
index 49e936848fe..2a2a6aa0ed7 100644
--- a/addons/lunch/tests/test_lunch.py
+++ b/addons/lunch/tests/test_lunch.py
@@ -58,13 +58,13 @@ class Test_Lunch(common.TransactionCase):
self.order_one = self.lunch_order_line.browse(cr,uid,self.new_id_order_line,context=None)
#we check that our order_line is a 'new' one and that there are no cashmove linked to that order_line:
self.assertEqual(self.order_one.state,'new')
- self.assertEqual(self.order_one.cashmove, [])
+ self.assertEqual(list(self.order_one.cashmove), [])
#we order that orderline so it's state will be 'ordered'
self.order_one.order()
self.order_one = self.lunch_order_line.browse(cr,uid,self.new_id_order_line,context=None)
#we check that our order_line is a 'ordered' one and that there are no cashmove linked to that order_line:
self.assertEqual(self.order_one.state,'ordered')
- self.assertEqual(self.order_one.cashmove, [])
+ self.assertEqual(list(self.order_one.cashmove), [])
def test_01_lunch_order(self):
"""Change the state of an order line from 'new' to 'ordered' then to 'confirmed'. Check that there is a cashmove linked to the order line"""
@@ -76,7 +76,7 @@ class Test_Lunch(common.TransactionCase):
self.order_one = self.lunch_order_line.browse(cr,uid,self.new_id_order_line,context=None)
#we check that our order_line is a 'confirmed' one and that there are a cashmove linked to that order_line with an amount equals to the order line price:
self.assertEqual(self.order_one.state,'confirmed')
- self.assertTrue(self.order_one.cashmove!=[])
+ self.assertTrue(self.order_one.cashmove)
self.assertTrue(self.order_one.cashmove[0].amount==-self.order_one.price)
def test_02_lunch_order(self):
@@ -89,4 +89,4 @@ class Test_Lunch(common.TransactionCase):
self.order_one = self.lunch_order_line.browse(cr,uid,self.new_id_order_line,context=None)
#We check that the state is cancelled and that the cashmove has been deleted
self.assertEqual(self.order_one.state,'cancelled')
- self.assertTrue(self.order_one.cashmove==[])
\ No newline at end of file
+ self.assertFalse(self.order_one.cashmove)
\ No newline at end of file
diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py
index 2045353b0e5..42238e75553 100644
--- a/addons/mail/mail_followers.py
+++ b/addons/mail/mail_followers.py
@@ -48,6 +48,25 @@ class mail_followers(osv.Model):
help="Message subtypes followed, meaning subtypes that will be pushed onto the user's Wall."),
}
+ #
+ # Modifying followers change access rights to individual documents. As the
+ # cache may contain accessible/inaccessible data, one has to refresh it.
+ #
+ def create(self, cr, uid, vals, context=None):
+ res = super(mail_followers, self).create(cr, uid, vals, context=context)
+ self.invalidate_cache(cr, uid, context=context)
+ return res
+
+ def write(self, cr, uid, ids, vals, context=None):
+ res = super(mail_followers, self).write(cr, uid, ids, vals, context=context)
+ self.invalidate_cache(cr, uid, context=context)
+ return res
+
+ def unlink(self, cr, uid, ids, context=None):
+ res = super(mail_followers, self).unlink(cr, uid, ids, context=context)
+ self.invalidate_cache(cr, uid, context=context)
+ return res
+
class mail_notification(osv.Model):
""" Class holding notifications pushed to partners. Followers and partners
@@ -60,7 +79,7 @@ class mail_notification(osv.Model):
_columns = {
'partner_id': fields.many2one('res.partner', string='Contact',
ondelete='cascade', required=True, select=1),
- 'read': fields.boolean('Read', select=1),
+ 'is_read': fields.boolean('Read', select=1),
'starred': fields.boolean('Starred', select=1,
help='Starred message that goes into the todo mailbox'),
'message_id': fields.many2one('mail.message', string='Message',
@@ -68,14 +87,14 @@ class mail_notification(osv.Model):
}
_defaults = {
- 'read': False,
+ 'is_read': False,
'starred': False,
}
def init(self, cr):
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_partner_id_read_starred_message_id',))
if not cr.fetchone():
- cr.execute('CREATE INDEX mail_notification_partner_id_read_starred_message_id ON mail_notification (partner_id, read, starred, message_id)')
+ cr.execute('CREATE INDEX mail_notification_partner_id_read_starred_message_id ON mail_notification (partner_id, is_read, starred, message_id)')
def get_partners_to_email(self, cr, uid, ids, message, context=None):
""" Return the list of partners to notify, based on their preferences.
@@ -86,7 +105,7 @@ class mail_notification(osv.Model):
"""
notify_pids = []
for notification in self.browse(cr, uid, ids, context=context):
- if notification.read:
+ if notification.is_read:
continue
partner = notification.partner_id
# Do not send to partners without email address defined
@@ -148,12 +167,12 @@ class mail_notification(osv.Model):
existing_pids.add(notification.partner_id.id)
# update existing notifications
- self.write(cr, uid, ids, {'read': False}, context=context)
+ self.write(cr, uid, ids, {'is_read': False}, context=context)
# create new notifications
new_pids = set(partner_ids) - existing_pids
for new_pid in new_pids:
- new_notif_ids.append(self.create(cr, uid, {'message_id': message_id, 'partner_id': new_pid, 'read': False}, context=context))
+ new_notif_ids.append(self.create(cr, uid, {'message_id': message_id, 'partner_id': new_pid, 'is_read': False}, context=context))
return new_notif_ids
def _notify_email(self, cr, uid, ids, message_id, force_send=False, user_signature=True, context=None):
diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py
index 9881e6422c6..eaca5c1182e 100644
--- a/addons/mail/mail_group.py
+++ b/addons/mail/mail_group.py
@@ -37,7 +37,7 @@ class mail_group(osv.Model):
_inherits = {'mail.alias': 'alias_id'}
def _get_image(self, cr, uid, ids, name, args, context=None):
- result = dict.fromkeys(ids, False)
+ result = {}
for obj in self.browse(cr, uid, ids, context=context):
result[obj.id] = tools.image_get_resized_images(obj.image)
return result
@@ -163,15 +163,14 @@ class mail_group(osv.Model):
def unlink(self, cr, uid, ids, context=None):
groups = self.browse(cr, uid, ids, context=context)
- # Cascade-delete mail aliases as well, as they should not exist without the mail group.
- mail_alias = self.pool.get('mail.alias')
alias_ids = [group.alias_id.id for group in groups if group.alias_id]
+ menu_ids = [group.menu_id.id for group in groups if group.menu_id]
# Delete mail_group
res = super(mail_group, self).unlink(cr, uid, ids, context=context)
- # Delete alias
- mail_alias.unlink(cr, SUPERUSER_ID, alias_ids, context=context)
+ # Cascade-delete mail aliases as well, as they should not exist without the mail group.
+ self.pool.get('mail.alias').unlink(cr, SUPERUSER_ID, alias_ids, context=context)
# Cascade-delete menu entries as well
- self.pool.get('ir.ui.menu').unlink(cr, SUPERUSER_ID, [group.menu_id.id for group in groups if group.menu_id], context=context)
+ self.pool.get('ir.ui.menu').unlink(cr, SUPERUSER_ID, menu_ids, context=context)
return res
def write(self, cr, uid, ids, vals, context=None):
diff --git a/addons/mail/mail_group_menu.py b/addons/mail/mail_group_menu.py
index 56d4b5dd564..3e849e62ac5 100644
--- a/addons/mail/mail_group_menu.py
+++ b/addons/mail/mail_group_menu.py
@@ -42,7 +42,7 @@ class ir_ui_menu(osv.osv):
following. Access are done using SUPERUSER_ID to avoid access
rights issues for an internal back-end algorithm. """
ids = super(ir_ui_menu, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=False)
- partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
+ partner_id = self.pool.get('res.users').read(cr, uid, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
follower_obj = self.pool.get('mail.followers')
for menu in self.browse(cr, uid, ids, context=context):
if menu.mail_group_id:
diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py
index 0a6a250bcaa..aeea2c0b1a5 100644
--- a/addons/mail/mail_mail.py
+++ b/addons/mail/mail_mail.py
@@ -24,7 +24,7 @@ import logging
import re
from urlparse import urljoin
-from openerp import tools
+from openerp import api, tools
from openerp import SUPERUSER_ID
from openerp.addons.base.ir.ir_mail_server import MailDeliveryException
from openerp.osv import fields, osv
@@ -51,7 +51,7 @@ class mail_mail(osv.Model):
('received', 'Received'),
('exception', 'Delivery Failed'),
('cancel', 'Cancelled'),
- ], 'Status', readonly=True),
+ ], 'Status', readonly=True, copy=False),
'auto_delete': fields.boolean('Auto Delete',
help="Permanently delete this email after sending it, to save space"),
'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1),
@@ -59,7 +59,7 @@ class mail_mail(osv.Model):
'recipient_ids': fields.many2many('res.partner', string='To (Partners)'),
'email_cc': fields.char('Cc', help='Carbon copy message recipients'),
'body_html': fields.text('Rich-text Contents', help="Rich-text/HTML message"),
- 'headers': fields.text('Headers'),
+ 'headers': fields.text('Headers', copy=False),
# Auto-detected based on create() - if 'mail_message_id' was passed then this mail is a notification
# and during unlink() we will not cascade delete the parent and its attachments
'notification': fields.boolean('Is Notification',
@@ -98,6 +98,7 @@ class mail_mail(osv.Model):
def cancel(self, cr, uid, ids, context=None):
return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
+ @api.cr_uid
def process_email_queue(self, cr, uid, ids=None, context=None):
"""Send immediately queued messages, committing after each
message is sent - this is not transactional and should
@@ -226,8 +227,7 @@ class mail_mail(osv.Model):
email sending process has failed
:return: True
"""
- if context is None:
- context = {}
+ context = dict(context or {})
ir_mail_server = self.pool.get('ir.mail_server')
ir_attachment = self.pool['ir.attachment']
for mail in self.browse(cr, SUPERUSER_ID, ids, context=context):
@@ -301,8 +301,9 @@ class mail_mail(osv.Model):
# /!\ can't use mail.state here, as mail.refresh() will cause an error
# see revid:odo@openerp.com-20120622152536-42b2s28lvdv3odyr in 6.1
+ if mail_sent:
+ _logger.info('Mail with ID %r and Message-Id %r successfully sent', mail.id, mail.message_id)
self._postprocess_sent_message(cr, uid, mail, context=context, mail_sent=mail_sent)
- _logger.info('Mail with ID %r and Message-Id %r successfully sent', mail.id, mail.message_id)
except MemoryError:
# prevent catching transient MemoryErrors, bubble up to notify user or abort cron job
# instead of marking the mail as failed
diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py
index 42aaef80703..d11495289f7 100644
--- a/addons/mail/mail_message.py
+++ b/addons/mail/mail_message.py
@@ -24,7 +24,7 @@ import logging
from openerp import tools
from email.header import decode_header
-from openerp import SUPERUSER_ID
+from openerp import SUPERUSER_ID, api
from openerp.osv import osv, orm, fields
from openerp.tools import html_email_clean
from openerp.tools.translate import _
@@ -88,7 +88,7 @@ class mail_message(osv.Model):
notif_ids = notif_obj.search(cr, uid, [
('partner_id', 'in', [partner_id]),
('message_id', 'in', ids),
- ('read', '=', False),
+ ('is_read', '=', False),
], context=context)
for notif in notif_obj.browse(cr, uid, notif_ids, context=context):
res[notif.message_id.id] = True
@@ -96,8 +96,8 @@ class mail_message(osv.Model):
def _search_to_read(self, cr, uid, obj, name, domain, context=None):
""" Search for messages to read by the current user. Condition is
- inversed because we search unread message on a read column. """
- return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.read', '=', not domain[0][2])]
+ inversed because we search unread message on a is_read column. """
+ return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.is_read', '=', not domain[0][2])]
def _get_starred(self, cr, uid, ids, name, arg, context=None):
""" Compute if the message is unread by the current user. """
@@ -114,8 +114,7 @@ class mail_message(osv.Model):
return res
def _search_starred(self, cr, uid, obj, name, domain, context=None):
- """ Search for messages to read by the current user. Condition is
- inversed because we search unread message on a read column. """
+ """ Search for starred messages by the current user."""
return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.starred', '=', domain[0][2])]
_columns = {
@@ -153,7 +152,7 @@ class mail_message(osv.Model):
help='Technical field holding the message notifications. Use notified_partner_ids to access notified partners.'),
'subject': fields.char('Subject'),
'date': fields.datetime('Date'),
- 'message_id': fields.char('Message-Id', help='Message unique identifier', select=1, readonly=1),
+ 'message_id': fields.char('Message-Id', help='Message unique identifier', select=1, readonly=1, copy=False),
'body': fields.html('Contents', help='Automatically sanitized HTML contents'),
'to_read': fields.function(_get_to_read, fnct_search=_search_to_read,
type='boolean', string='To read',
@@ -213,8 +212,9 @@ class mail_message(osv.Model):
def download_attachment(self, cr, uid, id_message, attachment_id, context=None):
""" Return the content of linked attachments. """
- message = self.browse(cr, uid, id_message, context=context)
- if attachment_id in [attachment.id for attachment in message.attachment_ids]:
+ # this will fail if you cannot read the message
+ message_values = self.read(cr, uid, [id_message], ['attachment_ids'], context=context)[0]
+ if attachment_id in message_values['attachment_ids']:
attachment = self.pool.get('ir.attachment').browse(cr, SUPERUSER_ID, attachment_id, context=context)
if attachment.datas and attachment.datas_fname:
return {
@@ -227,6 +227,7 @@ class mail_message(osv.Model):
# Notification API
#------------------------------------------------------
+ @api.cr_uid_ids_context
def set_message_read(self, cr, uid, msg_ids, read, create_missing=True, context=None):
""" Set messages as (un)read. Technically, the notifications related
to uid are set to (un)read. If for some msg_ids there are missing
@@ -243,22 +244,23 @@ class mail_message(osv.Model):
user_pid = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context).partner_id.id
domain = [('partner_id', '=', user_pid), ('message_id', 'in', msg_ids)]
if not create_missing:
- domain += [('read', '=', not read)]
+ domain += [('is_read', '=', not read)]
notif_ids = notification_obj.search(cr, uid, domain, context=context)
# all message have notifications: already set them as (un)read
if len(notif_ids) == len(msg_ids) or not create_missing:
- notification_obj.write(cr, uid, notif_ids, {'read': read}, context=context)
+ notification_obj.write(cr, uid, notif_ids, {'is_read': read}, context=context)
return len(notif_ids)
# some messages do not have notifications: find which one, create notification, update read status
notified_msg_ids = [notification.message_id.id for notification in notification_obj.browse(cr, uid, notif_ids, context=context)]
to_create_msg_ids = list(set(msg_ids) - set(notified_msg_ids))
for msg_id in to_create_msg_ids:
- notification_obj.create(cr, uid, {'partner_id': user_pid, 'read': read, 'message_id': msg_id}, context=context)
- notification_obj.write(cr, uid, notif_ids, {'read': read}, context=context)
+ notification_obj.create(cr, uid, {'partner_id': user_pid, 'is_read': read, 'message_id': msg_id}, context=context)
+ notification_obj.write(cr, uid, notif_ids, {'is_read': read}, context=context)
return len(notif_ids)
+ @api.cr_uid_ids_context
def set_message_starred(self, cr, uid, msg_ids, starred, create_missing=True, context=None):
""" Set messages as (un)starred. Technically, the notifications related
to uid are set to (un)starred.
@@ -276,7 +278,7 @@ class mail_message(osv.Model):
'starred': starred
}
if starred:
- values['read'] = False
+ values['is_read'] = False
notif_ids = notification_obj.search(cr, uid, domain, context=context)
@@ -502,6 +504,7 @@ class mail_message(osv.Model):
return True
+ @api.cr_uid_context
def message_read(self, cr, uid, ids=None, domain=None, message_unload_ids=None,
thread_level=0, context=None, parent_id=False, limit=None):
""" Read messages from mail.message, and get back a list of structured
@@ -671,7 +674,7 @@ class mail_message(osv.Model):
- uid has write or create access on the related document if model, res_id
- otherwise: raise
"""
- def _generate_model_record_ids(msg_val, msg_ids=[]):
+ def _generate_model_record_ids(msg_val, msg_ids):
""" :param model_record_ids: {'model': {'res_id': (msg_id, msg_id)}, ... }
:param message_values: {'msg_id': {'model': .., 'res_id': .., 'author_id': ..}}
"""
@@ -782,8 +785,7 @@ class mail_message(osv.Model):
return message_id
def create(self, cr, uid, values, context=None):
- if context is None:
- context = {}
+ context = dict(context or {})
default_starred = context.pop('default_starred', False)
if 'email_from' not in values: # needed to compute reply_to
@@ -830,13 +832,6 @@ class mail_message(osv.Model):
self.pool.get('ir.attachment').unlink(cr, uid, attachments_to_delete, context=context)
return super(mail_message, self).unlink(cr, uid, ids, context=context)
- def copy(self, cr, uid, id, default=None, context=None):
- """ Overridden to avoid duplicating fields that are unique to each email """
- if default is None:
- default = {}
- default.update(message_id=False, headers=False)
- return super(mail_message, self).copy(cr, uid, id, default=default, context=context)
-
#------------------------------------------------------
# Messaging API
#------------------------------------------------------
@@ -888,5 +883,5 @@ class mail_message(osv.Model):
notification_obj.create(cr, uid, {
'message_id': message.parent_id.id,
'partner_id': partner.id,
- 'read': True,
+ 'is_read': True,
}, context=context)
diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py
index 54f2b8d8f8b..ca040597240 100644
--- a/addons/mail/mail_thread.py
+++ b/addons/mail/mail_thread.py
@@ -39,11 +39,11 @@ import re
from email.message import Message
from urllib import urlencode
-from openerp import tools
+from openerp import api, tools
from openerp import SUPERUSER_ID
from openerp.addons.mail.mail_message import decode
from openerp.osv import fields, osv, orm
-from openerp.osv.orm import browse_record, browse_null
+from openerp.osv.orm import BaseModel
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
@@ -154,12 +154,12 @@ class mail_thread(osv.AbstractModel):
- message_unread: has uid unread message for the document
- message_summary: html snippet summarizing the Chatter for kanban views """
res = dict((id, dict(message_unread=False, message_unread_count=0, message_summary=' ')) for id in ids)
- user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
+ user_pid = self.pool.get('res.users').read(cr, uid, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
# search for unread messages, directly in SQL to improve performances
cr.execute(""" SELECT m.res_id FROM mail_message m
RIGHT JOIN mail_notification n
- ON (n.message_id = m.id AND n.partner_id = %s AND (n.read = False or n.read IS NULL))
+ ON (n.message_id = m.id AND n.partner_id = %s AND (n.is_read = False or n.is_read IS NULL))
WHERE m.model = %s AND m.res_id in %s""",
(user_pid, self._name, tuple(ids),))
for result in cr.fetchall():
@@ -192,7 +192,7 @@ class mail_thread(osv.AbstractModel):
available, which are followed if any """
res = dict((id, dict(message_subtype_data='')) for id in ids)
if user_pid is None:
- user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
+ user_pid = self.pool.get('res.users').read(cr, uid, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
# find current model subtypes, add them to a dictionary
subtype_obj = self.pool.get('mail.message.subtype')
@@ -232,7 +232,7 @@ class mail_thread(osv.AbstractModel):
fol_obj = self.pool.get('mail.followers')
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [('res_model', '=', self._name), ('res_id', 'in', ids)])
res = dict((id, dict(message_follower_ids=[], message_is_follower=False)) for id in ids)
- user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
+ user_pid = self.pool.get('res.users').read(cr, uid, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
for fol in fol_obj.browse(cr, SUPERUSER_ID, fol_ids):
res[fol.res_id]['message_follower_ids'].append(fol.partner_id.id)
if fol.partner_id.id == user_pid:
@@ -448,10 +448,6 @@ class mail_thread(osv.AbstractModel):
def copy_data(self, cr, uid, id, default=None, context=None):
# avoid tracking multiple temporary changes during copy
context = dict(context or {}, mail_notrack=True)
-
- default = default or {}
- default['message_ids'] = []
- default['message_follower_ids'] = []
return super(mail_thread, self).copy_data(cr, uid, id, default=default, context=context)
#------------------------------------------------------
@@ -621,7 +617,7 @@ class mail_thread(osv.AbstractModel):
# default action is the Inbox action
self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context)
act_model, act_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, *self._get_inbox_action_xml_id(cr, uid, context=context))
- action = self.pool.get(act_model).read(cr, uid, act_id, [])
+ action = self.pool.get(act_model).read(cr, uid, [act_id], [])[0]
params = context.get('params')
msg_id = model = res_id = None
@@ -1059,11 +1055,12 @@ class mail_thread(osv.AbstractModel):
def message_route_process(self, cr, uid, message, message_dict, routes, context=None):
# postpone setting message_dict.partner_ids after message_post, to avoid double notifications
+ context = dict(context or {})
partner_ids = message_dict.pop('partner_ids', [])
thread_id = False
for model, thread_id, custom_values, user_id, alias in routes:
if self._name == 'mail.thread':
- context.update({'thread_model': model})
+ context['thread_model'] = model
if model:
model_pool = self.pool[model]
if not (thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new')):
@@ -1508,6 +1505,7 @@ class mail_thread(osv.AbstractModel):
m2m_attachment_ids.append((0, 0, data_attach))
return m2m_attachment_ids
+ @api.cr_uid_ids_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):
@@ -1807,10 +1805,8 @@ class mail_thread(osv.AbstractModel):
record = self.browse(cr, uid, ids[0], context=context)
for updated_field in updated_fields:
field_value = getattr(record, updated_field)
- if isinstance(field_value, browse_record):
+ if isinstance(field_value, BaseModel):
field_value = field_value.id
- elif isinstance(field_value, browse_null):
- field_value = False
values[updated_field] = field_value
# find followers of headers, update structure for new followers
@@ -1870,11 +1866,12 @@ class mail_thread(osv.AbstractModel):
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
cr.execute('''
UPDATE mail_notification SET
- read=false
+ is_read=false
WHERE
message_id IN (SELECT id from mail_message where res_id=any(%s) and model=%s limit 1) and
partner_id = %s
''', (ids, self._name, partner_id))
+ self.pool.get('mail.notification').invalidate_cache(cr, uid, ['is_read'], context=context)
return True
def message_mark_as_read(self, cr, uid, ids, context=None):
@@ -1882,11 +1879,12 @@ class mail_thread(osv.AbstractModel):
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
cr.execute('''
UPDATE mail_notification SET
- read=true
+ is_read=true
WHERE
message_id IN (SELECT id FROM mail_message WHERE res_id=ANY(%s) AND model=%s) AND
partner_id = %s
''', (ids, self._name, partner_id))
+ self.pool.get('mail.notification').invalidate_cache(cr, uid, ['is_read'], context=context)
return True
#------------------------------------------------------
diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py
index e218ea85927..a4b4e68942b 100644
--- a/addons/mail/res_users.py
+++ b/addons/mail/res_users.py
@@ -39,7 +39,7 @@ class res_users(osv.Model):
_columns = {
'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True,
help="Email address internally associated with this user. Incoming "\
- "emails will appear in the user's notifications."),
+ "emails will appear in the user's notifications.", copy=False),
'display_groups_suggestions': fields.boolean("Display Groups Suggestions"),
}
diff --git a/addons/mail/static/scripts/__init__.py b/addons/mail/static/scripts/__init__.py
deleted file mode 100644
index 3b6a8c7e4b0..00000000000
--- a/addons/mail/static/scripts/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2009-2010 OpenERP SA ().
-#
-# 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 openerp_mailgate
\ No newline at end of file
diff --git a/addons/mail/static/scripts/openerp_mailgate.py b/addons/mail/static/scripts/openerp_mailgate.py
index 99ebc8f770b..e3dbdf4adc0 100755
--- a/addons/mail/static/scripts/openerp_mailgate.py
+++ b/addons/mail/static/scripts/openerp_mailgate.py
@@ -30,10 +30,10 @@ import optparse
import sys
import xmlrpclib
import smtplib
-from email.MIMEMultipart import MIMEMultipart
-from email.MIMEBase import MIMEBase
-from email.MIMEText import MIMEText
-from email.Utils import COMMASPACE, formatdate
+from email.mime.multipart import MIMEMultipart
+from email.mime.base import MIMEBase
+from email.mime.text import MIMEText
+from email.utils import COMMASPACE, formatdate
from email import Encoders
class DefaultConfig(object):
diff --git a/addons/mail/static/scripts/openerp_mailgate/__init__.py b/addons/mail/static/scripts/openerp_mailgate/__init__.py
deleted file mode 100644
index de2e2bf390d..00000000000
--- a/addons/mail/static/scripts/openerp_mailgate/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2009-2010 OpenERP SA ().
-#
-# 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 openerp_mailgate
diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js
index bf83fe6d7c6..bd84dac714d 100644
--- a/addons/mail/static/src/js/mail_followers.js
+++ b/addons/mail/static/src/js/mail_followers.js
@@ -172,8 +172,8 @@ openerp_mail_followers = function(session, mail) {
fetch_generic: function (error, event) {
var self = this;
event.preventDefault();
- return this.ds_users.call('read', [this.session.uid, ['partner_id']]).then(function (results) {
- var pid = results['partner_id'][0];
+ return this.ds_users.call('read', [[this.session.uid], ['partner_id']]).then(function (results) {
+ var pid = results[0]['partner_id'][0];
self.message_is_follower = (_.indexOf(self.value, pid) != -1);
}).then(self.proxy('display_generic'));
},
diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py
index d53a8078c96..5a5c28f95d7 100644
--- a/addons/mail/tests/common.py
+++ b/addons/mail/tests/common.py
@@ -24,30 +24,28 @@ from openerp.tests import common
class TestMail(common.TransactionCase):
- def _mock_smtp_gateway(self, *args, **kwargs):
- return args[2]['Message-Id']
-
def _init_mock_build_email(self):
self._build_email_args_list = []
self._build_email_kwargs_list = []
- def _mock_build_email(self, *args, **kwargs):
- """ Mock build_email to be able to test its values. Store them into
- some internal variable for latter processing. """
- self._build_email_args_list.append(args)
- self._build_email_kwargs_list.append(kwargs)
- return self._build_email(*args, **kwargs)
-
def setUp(self):
super(TestMail, self).setUp()
cr, uid = self.cr, self.uid
# Install mock SMTP gateway
+ test = self
+
+ def build_email(self, *args, **kwargs):
+ test._build_email_args_list.append(args)
+ test._build_email_kwargs_list.append(kwargs)
+ return build_email.origin(self, *args, **kwargs)
+
+ def send_email(self, cr, uid, message, *args, **kwargs):
+ return message['Message-Id']
+
self._init_mock_build_email()
- self._build_email = self.registry('ir.mail_server').build_email
- self.registry('ir.mail_server').build_email = self._mock_build_email
- self._send_email = self.registry('ir.mail_server').send_email
- self.registry('ir.mail_server').send_email = self._mock_smtp_gateway
+ self.registry('ir.mail_server')._patch_method('build_email', build_email)
+ self.registry('ir.mail_server')._patch_method('send_email', send_email)
# Usefull models
self.ir_model = self.registry('ir.model')
@@ -129,6 +127,6 @@ class TestMail(common.TransactionCase):
def tearDown(self):
# Remove mocks
- self.registry('ir.mail_server').build_email = self._build_email
- self.registry('ir.mail_server').send_email = self._send_email
+ self.registry('ir.mail_server')._revert_method('build_email')
+ self.registry('ir.mail_server')._revert_method('send_email')
super(TestMail, self).tearDown()
diff --git a/addons/mail/tests/test_mail_features.py b/addons/mail/tests/test_mail_features.py
index 2bf7bb91264..fdd87bbcfab 100644
--- a/addons/mail/tests/test_mail_features.py
+++ b/addons/mail/tests/test_mail_features.py
@@ -280,7 +280,7 @@ class test_mail(TestMail):
self.assertNotIn('res_id=%s' % group_pigs.id, url,
'notification email: link based on message should not contain res_id')
- @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm')
+ @mute_logger('openerp.addons.mail.mail_thread', 'openerp.models')
def test_12_inbox_redirection(self):
""" Tests designed to test the inbox redirection of emails notification URLs. """
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
@@ -654,7 +654,7 @@ class test_mail(TestMail):
{
'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])]
}, context={
- 'default_composition_mode': 'reply',
+ 'default_composition_mode': 'comment',
'default_res_id': self.group_pigs_id,
'default_parent_id': message.id
})
@@ -772,14 +772,13 @@ class test_mail(TestMail):
def test_30_needaction(self):
""" Tests for mail.message needaction. """
cr, uid, user_admin, user_raoul, group_pigs = self.cr, self.uid, self.user_admin, self.user_raoul, self.group_pigs
- group_pigs_demo = self.mail_group.browse(cr, self.user_raoul_id, self.group_pigs_id)
na_admin_base = self.mail_message._needaction_count(cr, uid, domain=[])
na_demo_base = self.mail_message._needaction_count(cr, user_raoul.id, domain=[])
# Test: number of unread notification = needaction on mail.message
notif_ids = self.mail_notification.search(cr, uid, [
('partner_id', '=', user_admin.partner_id.id),
- ('read', '=', False)
+ ('is_read', '=', False)
])
na_count = self.mail_message._needaction_count(cr, uid, domain=[])
self.assertEqual(len(notif_ids), na_count, 'unread notifications count does not match needaction count')
@@ -787,13 +786,14 @@ class test_mail(TestMail):
# Do: post 2 message on group_pigs as admin, 3 messages as demo user
for dummy in range(2):
group_pigs.message_post(body='My Body', subtype='mt_comment')
+ raoul_pigs = group_pigs.sudo(user_raoul)
for dummy in range(3):
- group_pigs_demo.message_post(body='My Demo Body', subtype='mt_comment')
+ raoul_pigs.message_post(body='My Demo Body', subtype='mt_comment')
# Test: admin has 3 new notifications (from demo), and 3 new needaction
notif_ids = self.mail_notification.search(cr, uid, [
('partner_id', '=', user_admin.partner_id.id),
- ('read', '=', False)
+ ('is_read', '=', False)
])
self.assertEqual(len(notif_ids), na_admin_base + 3, 'Admin should have 3 new unread notifications')
na_admin = self.mail_message._needaction_count(cr, uid, domain=[])
@@ -803,7 +803,7 @@ class test_mail(TestMail):
# Test: demo has 0 new notifications (not a follower, not receiving its own messages), and 0 new needaction
notif_ids = self.mail_notification.search(cr, uid, [
('partner_id', '=', user_raoul.partner_id.id),
- ('read', '=', False)
+ ('is_read', '=', False)
])
self.assertEqual(len(notif_ids), na_demo_base + 0, 'Demo should have 0 new unread notifications')
na_demo = self.mail_message._needaction_count(cr, user_raoul.id, domain=[])
diff --git a/addons/mail/tests/test_mail_gateway.py b/addons/mail/tests/test_mail_gateway.py
index 3fbf3d40405..1b16f151cdf 100644
--- a/addons/mail/tests/test_mail_gateway.py
+++ b/addons/mail/tests/test_mail_gateway.py
@@ -164,7 +164,7 @@ class TestMailgateway(TestMail):
self.assertIn('
Should create a multipart/mixed: from gmail, bold, with attachment.
', res.get('body', ''),
'message_parse: html version should be in body after parsing multipart/mixed')
- @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm')
+ @mute_logger('openerp.addons.mail.mail_thread', 'openerp.models')
def test_10_message_process(self):
""" Testing incoming emails processing. """
cr, uid, user_raoul = self.cr, self.uid, self.user_raoul
@@ -203,8 +203,8 @@ class TestMailgateway(TestMail):
# Test: one group created by mailgateway administrator
self.assertEqual(len(frog_groups), 1, 'message_process: a new mail.group should have been created')
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
- res = self.mail_group.perm_read(cr, uid, [frog_group.id], details=False)
- self.assertEqual(res[0].get('create_uid'), uid,
+ res = self.mail_group.get_metadata(cr, uid, [frog_group.id])[0].get('create_uid') or [None]
+ self.assertEqual(res[0], uid,
'message_process: group should have been created by uid as alias_user__id is False on the alias')
# Test: one message that is the incoming email
self.assertEqual(len(frog_group.message_ids), 1,
@@ -271,8 +271,8 @@ class TestMailgateway(TestMail):
# Test: one group created by Raoul
self.assertEqual(len(frog_groups), 1, 'message_process: a new mail.group should have been created')
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
- res = self.mail_group.perm_read(cr, uid, [frog_group.id], details=False)
- self.assertEqual(res[0].get('create_uid'), self.user_raoul_id,
+ res = self.mail_group.get_metadata(cr, uid, [frog_group.id])[0].get('create_uid') or [None]
+ self.assertEqual(res[0], self.user_raoul_id,
'message_process: group should have been created by alias_user_id')
# Test: one message that is the incoming email
self.assertEqual(len(frog_group.message_ids), 1,
@@ -536,7 +536,7 @@ class TestMailgateway(TestMail):
self.assertIn('
\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n
', msg.body,
'message_process: plaintext incoming email incorrectly parsed')
- @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm')
+ @mute_logger('openerp.addons.mail.mail_thread', 'openerp.models')
def test_20_thread_parent_resolution(self):
""" Testing parent/child relationships are correctly established when processing incoming mails """
cr, uid = self.cr, self.uid
diff --git a/addons/mail/tests/test_mail_group.py b/addons/mail/tests/test_mail_group.py
index 34c7f0c00e0..c131ce0bd0b 100644
--- a/addons/mail/tests/test_mail_group.py
+++ b/addons/mail/tests/test_mail_group.py
@@ -27,7 +27,7 @@ from openerp.tools import mute_logger
class TestMailGroup(TestMail):
- @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
+ @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
def test_00_mail_group_access_rights(self):
""" Testing mail_group access rights and basic mail_thread features """
cr, uid, user_noone_id, user_employee_id = self.cr, self.uid, self.user_noone_id, self.user_employee_id
diff --git a/addons/mail/tests/test_mail_message.py b/addons/mail/tests/test_mail_message.py
index d4a7ae75df3..40d6786e2c9 100644
--- a/addons/mail/tests/test_mail_message.py
+++ b/addons/mail/tests/test_mail_message.py
@@ -161,7 +161,7 @@ class TestMailMessage(TestMail):
self.assertEqual(mail.reply_to, 'someone@example.com',
'mail_mail: reply_to should equal the rpely_to given to create')
- @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
+ @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
def test_10_mail_message_search_access_rights(self):
""" Testing mail_message.search() using specific _search implementation """
cr, uid, group_pigs_id = self.cr, self.uid, self.group_pigs_id
@@ -196,7 +196,7 @@ class TestMailMessage(TestMail):
msg_ids = self.mail_message.search(cr, uid, [('subject', 'like', '_Test')])
self.assertEqual(set([msg_id1, msg_id2, msg_id3, msg_id4, msg_id5, msg_id6, msg_id7, msg_id8]), set(msg_ids), 'mail_message search failed')
- @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
+ @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
def test_15_mail_message_check_access_rule(self):
""" Testing mail_message.check_access_rule() """
cr, uid = self.cr, self.uid
@@ -218,8 +218,8 @@ class TestMailMessage(TestMail):
message_id = self.mail_message.create(cr, uid, {'body': 'My Body', 'attachment_ids': [(4, attachment_id)]})
# Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
- self.assertRaises(except_orm, self.mail_message.read,
- cr, user_bert_id, message_id)
+ with self.assertRaises(except_orm):
+ self.mail_message.read(cr, user_bert_id, message_id)
# Do: message is pushed to Bert
notif_id = self.mail_notification.create(cr, uid, {'message_id': message_id, 'partner_id': partner_bert_id})
# Test: Bert reads the message, ok because notification pushed
@@ -229,11 +229,11 @@ class TestMailMessage(TestMail):
# Do: remove notification
self.mail_notification.unlink(cr, uid, notif_id)
# Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
- self.assertRaises(except_orm, self.mail_message.read,
- cr, self.user_bert_id, message_id)
+ with self.assertRaises(except_orm):
+ self.mail_message.read(cr, self.user_bert_id, message_id)
# Test: Bert downloads attachment, crash because he can't read message
- self.assertRaises(except_orm, self.mail_message.download_attachment,
- cr, user_bert_id, message_id, attachment_id)
+ with self.assertRaises(except_orm):
+ self.mail_message.download_attachment(cr, user_bert_id, message_id, attachment_id)
# Do: Bert is now the author
self.mail_message.write(cr, uid, [message_id], {'author_id': partner_bert_id})
# Test: Bert reads the message, ok because Bert is the author
@@ -241,8 +241,8 @@ class TestMailMessage(TestMail):
# Do: Bert is not the author anymore
self.mail_message.write(cr, uid, [message_id], {'author_id': partner_raoul_id})
# Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
- self.assertRaises(except_orm, self.mail_message.read,
- cr, user_bert_id, message_id)
+ with self.assertRaises(except_orm):
+ self.mail_message.read(cr, user_bert_id, message_id)
# Do: message is attached to a document Bert can read, Jobs
self.mail_message.write(cr, uid, [message_id], {'model': 'mail.group', 'res_id': self.group_jobs_id})
# Test: Bert reads the message, ok because linked to a doc he is allowed to read
@@ -250,33 +250,33 @@ class TestMailMessage(TestMail):
# Do: message is attached to a document Bert cannot read, Pigs
self.mail_message.write(cr, uid, [message_id], {'model': 'mail.group', 'res_id': self.group_pigs_id})
# Test: Bert reads the message, crash because not notification/not in doc followers/not read on doc
- self.assertRaises(except_orm, self.mail_message.read,
- cr, user_bert_id, message_id)
+ with self.assertRaises(except_orm):
+ self.mail_message.read(cr, user_bert_id, message_id)
# ----------------------------------------
# CASE2: create
# ----------------------------------------
# Do: Bert creates a message on Pigs -> ko, no creation rights
- self.assertRaises(AccessError, self.mail_message.create,
- cr, user_bert_id, {'model': 'mail.group', 'res_id': self.group_pigs_id, 'body': 'Test'})
+ with self.assertRaises(AccessError):
+ self.mail_message.create(cr, user_bert_id, {'model': 'mail.group', 'res_id': self.group_pigs_id, 'body': 'Test'})
# Do: Bert create a message on Jobs -> ko, no creation rights
- self.assertRaises(AccessError, self.mail_message.create,
- cr, user_bert_id, {'model': 'mail.group', 'res_id': self.group_jobs_id, 'body': 'Test'})
+ with self.assertRaises(AccessError):
+ self.mail_message.create(cr, user_bert_id, {'model': 'mail.group', 'res_id': self.group_jobs_id, 'body': 'Test'})
# Do: Bert create a private message -> ko, no creation rights
- self.assertRaises(AccessError, self.mail_message.create,
- cr, user_bert_id, {'body': 'Test'})
+ with self.assertRaises(AccessError):
+ self.mail_message.create(cr, user_bert_id, {'body': 'Test'})
# Do: Raoul creates a message on Jobs -> ok, write access to the related document
self.mail_message.create(cr, user_raoul_id, {'model': 'mail.group', 'res_id': self.group_jobs_id, 'body': 'Test'})
# Do: Raoul creates a message on Priv -> ko, no write access to the related document
- self.assertRaises(except_orm, self.mail_message.create,
- cr, user_raoul_id, {'model': 'mail.group', 'res_id': self.group_priv_id, 'body': 'Test'})
+ with self.assertRaises(except_orm):
+ self.mail_message.create(cr, user_raoul_id, {'model': 'mail.group', 'res_id': self.group_priv_id, 'body': 'Test'})
# Do: Raoul creates a private message -> ok
self.mail_message.create(cr, user_raoul_id, {'body': 'Test'})
# Do: Raoul creates a reply to a message on Priv -> ko
- self.assertRaises(except_orm, self.mail_message.create,
- cr, user_raoul_id, {'model': 'mail.group', 'res_id': self.group_priv_id, 'body': 'Test', 'parent_id': priv_msg_id})
+ with self.assertRaises(except_orm):
+ self.mail_message.create(cr, user_raoul_id, {'model': 'mail.group', 'res_id': self.group_priv_id, 'body': 'Test', 'parent_id': priv_msg_id})
# Do: Raoul creates a reply to a message on Priv-> ok if has received parent
self.mail_notification.create(cr, uid, {'message_id': priv_msg_id, 'partner_id': self.partner_raoul_id})
self.mail_message.create(cr, user_raoul_id, {'model': 'mail.group', 'res_id': self.group_priv_id, 'body': 'Test', 'parent_id': priv_msg_id})
@@ -335,7 +335,7 @@ class TestMailMessage(TestMail):
self.assertEqual(len(notif_ids), 1, 'mail_message set_message_read: more than one notification created')
# Test: notification read
notif = self.mail_notification.browse(cr, uid, notif_ids[0])
- self.assertTrue(notif.read, 'mail_notification read failed')
+ self.assertTrue(notif['is_read'], 'mail_notification read failed')
self.assertFalse(msg.to_read, 'mail_message read failed')
# Do: Raoul reads msg
@@ -346,7 +346,7 @@ class TestMailMessage(TestMail):
self.assertEqual(len(notif_ids), 1, 'mail_message set_message_read: more than one notification created')
# Test: notification read
notif = self.mail_notification.browse(cr, uid, notif_ids[0])
- self.assertTrue(notif.read, 'mail_notification starred failed')
+ self.assertTrue(notif['is_read'], 'mail_notification starred failed')
self.assertFalse(msg_raoul.to_read, 'mail_message starred failed')
# Do: Admin unreads msg
@@ -383,7 +383,7 @@ class TestMailMessage(TestMail):
self.assertEqual(set(msg.vote_user_ids), set([self.user_raoul]), 'mail_message vote: after unvoting, Bert should be in the voter')
self.assertEqual(set(msg_raoul.vote_user_ids), set([self.user_raoul]), 'mail_message vote: after unvoting, Bert should be in the voter')
- @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
+ @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
def test_50_mail_flow_access_rights(self):
""" Test a Chatter-looks alike flow to test access rights """
cr, uid = self.cr, self.uid
@@ -400,9 +400,9 @@ class TestMailMessage(TestMail):
# ----------------------------------------
# Do: Bert reads Jobs basic fields, ok because public = read access on the group
- self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['name', 'description'])
+ self.mail_group.read(cr, user_bert_id, [self.group_jobs_id], ['name', 'description'])
# Do: Bert reads Jobs messages, ok because read access on the group => read access on its messages
- jobs_message_ids = self.mail_group.read(cr, user_bert_id, self.group_jobs_id, ['message_ids'])['message_ids']
+ jobs_message_ids = self.mail_group.read(cr, user_bert_id, [self.group_jobs_id], ['message_ids'])[0]['message_ids']
self.mail_message.read(cr, user_bert_id, jobs_message_ids)
# Do: Bert browses Jobs, ok (no direct browse of partners), ok for messages, ko for followers (accessible to employees or partner manager)
bert_jobs = self.mail_group.browse(cr, user_bert_id, self.group_jobs_id)
@@ -413,9 +413,8 @@ class TestMailMessage(TestMail):
with self.assertRaises(AccessError):
trigger_read = partner.name
# Do: Bert comments Jobs, ko because no creation right
- self.assertRaises(AccessError,
- self.mail_group.message_post,
- cr, user_bert_id, self.group_jobs_id, body='I love Pigs')
+ with self.assertRaises(AccessError):
+ self.mail_group.message_post(cr, user_bert_id, self.group_jobs_id, body='I love Pigs')
# Do: Bert writes on its own profile, ko because no message create access
with self.assertRaises(AccessError):
@@ -444,5 +443,5 @@ class TestMailMessage(TestMail):
# Do: Raoul replies to a Jobs message using the composer
compose_id = mail_compose.create(cr, user_raoul_id,
{'subject': 'Subject', 'body': 'Body text'},
- {'default_composition_mode': 'reply', 'default_parent_id': pigs_msg_id})
+ {'default_composition_mode': 'comment', 'default_parent_id': pigs_msg_id})
mail_compose.send_mail(cr, user_raoul_id, [compose_id])
diff --git a/addons/mail/tests/test_message_read.py b/addons/mail/tests/test_message_read.py
index c02e9a32278..fbf30f2ed22 100644
--- a/addons/mail/tests/test_message_read.py
+++ b/addons/mail/tests/test_message_read.py
@@ -46,7 +46,7 @@ class test_mail_access_rights(TestMail):
ordered_msg_ids = [msg_id2, msg_id4, msg_id6, msg_id8, msg_id10, msg_id1, msg_id3, msg_id5, msg_id7, msg_id9, msg_id0]
# Test: raoul received notifications
- raoul_notification_ids = self.mail_notification.search(cr, user_raoul.id, [('read', '=', False), ('message_id', 'in', msg_ids), ('partner_id', '=', user_raoul.partner_id.id)])
+ raoul_notification_ids = self.mail_notification.search(cr, user_raoul.id, [('is_read', '=', False), ('message_id', 'in', msg_ids), ('partner_id', '=', user_raoul.partner_id.id)])
self.assertEqual(len(raoul_notification_ids), 11, 'message_post: wrong number of produced notifications')
# Test: read some specific ids
diff --git a/addons/mail/update.py b/addons/mail/update.py
index d2873f3a5bf..a76c54ff2da 100644
--- a/addons/mail/update.py
+++ b/addons/mail/update.py
@@ -59,7 +59,7 @@ def get_sys_logs(self, cr, uid):
add_arg = {"timeout":30} if sys.version_info >= (2,6) else {}
arguments = {'arg0': msg, "action": "update",}
- arguments_raw = werkzeug.url_encode(arguments)
+ arguments_raw = werkzeug.urls.url_encode(arguments)
url = config.get("publisher_warranty_url")
diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py
index 0425cd1ee0e..41dc4a9b438 100644
--- a/addons/mail/wizard/mail_compose_message.py
+++ b/addons/mail/wizard/mail_compose_message.py
@@ -196,8 +196,7 @@ class mail_compose_message(osv.TransientModel):
def send_mail(self, cr, uid, ids, context=None):
""" Process the wizard content and proceed with sending the related
email(s), rendering any template patterns on the fly if needed. """
- if context is None:
- context = {}
+ context = dict(context or {})
# clean the context (hint: mass mailing sets some default values that
# could be wrongly interpreted by mail_mail)
diff --git a/addons/marketing_campaign/marketing_campaign.py b/addons/marketing_campaign/marketing_campaign.py
index 469750ddc5f..e8c2d68e660 100644
--- a/addons/marketing_campaign/marketing_campaign.py
+++ b/addons/marketing_campaign/marketing_campaign.py
@@ -31,6 +31,7 @@ from openerp.tools.safe_eval import safe_eval as eval
import re
from openerp.addons.decimal_precision import decimal_precision as dp
+from openerp import api
from openerp.osv import fields, osv
from openerp.report import render_report
from openerp.tools.translate import _
@@ -44,43 +45,6 @@ _intervalTypes = {
DT_FMT = '%Y-%m-%d %H:%M:%S'
-def dict_map(f, d):
- return dict((k, f(v)) for k,v in d.items())
-
-def _find_fieldname(model, field):
- inherit_columns = dict_map(itemgetter(2), model._inherit_fields)
- all_columns = dict(inherit_columns, **model._columns)
- for fn in all_columns:
- if all_columns[fn] is field:
- return fn
- raise ValueError('Field not found: %r' % (field,))
-
-class selection_converter(object):
- """Format the selection in the browse record objects"""
- def __init__(self, value):
- self._value = value
- self._str = value
-
- def set_value(self, cr, uid, _self_again, record, field, lang):
- # this design is terrible
- # search fieldname from the field
- fieldname = _find_fieldname(record._table, field)
- context = dict(lang=lang.code)
- fg = record._table.fields_get(cr, uid, [fieldname], context=context)
- selection = dict(fg[fieldname]['selection'])
- self._str = selection[self.value]
-
- @property
- def value(self):
- return self._value
-
- def __str__(self):
- return self._str
-
-translate_selections = {
- 'selection': selection_converter,
-}
-
class marketing_campaign(osv.osv):
_name = "marketing.campaign"
@@ -126,7 +90,7 @@ Normal - the campaign runs normally and automatically sends all emails and repor
('running', 'Running'),
('cancelled', 'Cancelled'),
('done', 'Done')],
- 'Status'),
+ 'Status', copy=False),
'activity_ids': fields.one2many('marketing.campaign.activity',
'campaign_id', 'Activities'),
'fixed_cost': fields.float('Fixed Cost', help="Fixed cost for running this campaign. You may also specify variable cost and revenue on each campaign activity. Cost and Revenue statistics are included in Campaign Reporting.", digits_compute=dp.get_precision('Product Price')),
@@ -186,7 +150,7 @@ Normal - the campaign runs normally and automatically sends all emails and repor
raise ValueError('Signal cannot be False.')
Workitems = self.pool.get('marketing.campaign.workitem')
- domain = [('object_id.model', '=', record._table._name),
+ domain = [('object_id.model', '=', record._name),
('state', '=', 'running')]
campaign_ids = self.search(cr, uid, domain, context=context)
for campaign in self.browse(cr, uid, campaign_ids, context=context):
@@ -282,7 +246,7 @@ class marketing_campaign_segment(osv.osv):
('cancelled', 'Cancelled'),
('running', 'Running'),
('done', 'Done')],
- 'Status'),
+ 'Status', copy=False),
'date_run': fields.datetime('Launch Date', help="Initial start date of this segment."),
'date_done': fields.datetime('End Date', help="Date this segment was last closed or cancelled."),
'date_next_sync': fields.function(_get_next_sync, string='Next Synchronization', type='datetime', help="Next time the synchronization job is scheduled to run automatically"),
@@ -344,6 +308,7 @@ class marketing_campaign_segment(osv.osv):
self.process_segment(cr, uid, ids)
return True
+ @api.cr_uid_ids_context
def process_segment(self, cr, uid, segment_ids=None, context=None):
Workitems = self.pool.get('marketing.campaign.workitem')
Campaigns = self.pool.get('marketing.campaign')
@@ -523,20 +488,30 @@ class marketing_campaign_transition(osv.osv):
_description = "Campaign Transition"
_interval_units = [
- ('hours', 'Hour(s)'), ('days', 'Day(s)'),
- ('months', 'Month(s)'), ('years','Year(s)')
+ ('hours', 'Hour(s)'),
+ ('days', 'Day(s)'),
+ ('months', 'Month(s)'),
+ ('years', 'Year(s)'),
]
def _get_name(self, cr, uid, ids, fn, args, context=None):
- result = dict.fromkeys(ids, False)
+ # name formatters that depend on trigger
formatters = {
'auto': _('Automatic transition'),
'time': _('After %(interval_nbr)d %(interval_type)s'),
'cosmetic': _('Cosmetic'),
}
- for tr in self.browse(cr, uid, ids, context=context,
- fields_process=translate_selections):
- result[tr.id] = formatters[tr.trigger.value] % tr
+ # get the translations of the values of selection field 'interval_type'
+ fields = self.fields_get(cr, uid, ['interval_type'], context=context)
+ interval_type_selection = dict(fields['interval_type']['selection'])
+
+ result = dict.fromkeys(ids, False)
+ for trans in self.browse(cr, uid, ids, context=context):
+ values = {
+ 'interval_nbr': trans.interval_nbr,
+ 'interval_type': interval_type_selection.get(trans.interval_type, ''),
+ }
+ result[trans.id] = formatters[trans.trigger] % values
return result
@@ -656,7 +631,7 @@ class marketing_campaign_workitem(osv.osv):
('cancelled', 'Cancelled'),
('exception', 'Exception'),
('done', 'Done'),
- ], 'Status', readonly=True),
+ ], 'Status', readonly=True, copy=False),
'error_msg' : fields.text('Error Message', readonly=True)
}
_defaults = {
@@ -664,12 +639,14 @@ class marketing_campaign_workitem(osv.osv):
'date': False,
}
+ @api.cr_uid_ids_context
def button_draft(self, cr, uid, workitem_ids, context=None):
for wi in self.browse(cr, uid, workitem_ids, context=context):
if wi.state in ('exception', 'cancelled'):
self.write(cr, uid, [wi.id], {'state':'todo'}, context=context)
return True
+ @api.cr_uid_ids_context
def button_cancel(self, cr, uid, workitem_ids, context=None):
for wi in self.browse(cr, uid, workitem_ids, context=context):
if wi.state in ('todo','exception'):
@@ -698,9 +675,9 @@ class marketing_campaign_workitem(osv.osv):
if condition:
if not eval(condition, eval_context):
if activity.keep_if_condition_not_met:
- workitem.write({'state': 'cancelled'}, context=context)
+ workitem.write({'state': 'cancelled'})
else:
- workitem.unlink(context=context)
+ workitem.unlink()
return
result = True
if campaign_mode in ('manual', 'active'):
@@ -711,11 +688,11 @@ class marketing_campaign_workitem(osv.osv):
values = dict(state='done')
if not workitem.date:
values['date'] = datetime.now().strftime(DT_FMT)
- workitem.write(values, context=context)
+ workitem.write(values)
if result:
# process _chain
- workitem = workitem.browse(context=context)[0] # reload
+ workitem.refresh() # reload
date = datetime.strptime(workitem.date, DT_FMT)
for transition in activity.to_ids:
@@ -760,9 +737,9 @@ class marketing_campaign_workitem(osv.osv):
except Exception:
tb = "".join(format_exception(*exc_info()))
- workitem.write({'state': 'exception', 'error_msg': tb},
- context=context)
+ workitem.write({'state': 'exception', 'error_msg': tb})
+ @api.cr_uid_ids_context
def process(self, cr, uid, workitem_ids, context=None):
for wi in self.browse(cr, uid, workitem_ids, context=context):
self._process_one(cr, uid, wi, context=context)
diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py
index a1077a3fffa..33e66a2d12c 100644
--- a/addons/mass_mailing/models/mass_mailing.py
+++ b/addons/mass_mailing/models/mass_mailing.py
@@ -343,7 +343,7 @@ class MassMailing(osv.Model):
'name': fields.char('Subject', required=True),
'email_from': fields.char('From', required=True),
'create_date': fields.datetime('Creation Date'),
- 'sent_date': fields.datetime('Sent Date', oldname='date'),
+ 'sent_date': fields.datetime('Sent Date', oldname='date', copy=False),
'body_html': fields.html('Body'),
'attachment_ids': fields.many2many(
'ir.attachment', 'mass_mailing_ir_attachments_rel',
@@ -355,7 +355,7 @@ class MassMailing(osv.Model):
),
'state': fields.selection(
[('draft', 'Draft'), ('test', 'Tested'), ('done', 'Sent')],
- string='Status', required=True,
+ string='Status', required=True, copy=False,
),
'color': fields.related(
'mass_mailing_campaign_id', 'color',
@@ -460,15 +460,9 @@ class MassMailing(osv.Model):
#------------------------------------------------------
def copy_data(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
mailing = self.browse(cr, uid, id, context=context)
- default.update({
- 'state': 'draft',
- 'statistics_ids': [],
- 'name': _('%s (duplicate)') % mailing.name,
- 'sent_date': False,
- })
+ default = dict(default or {},
+ name=_('%s (copy)') % mailing.name)
return super(MassMailing, self).copy_data(cr, uid, id, default, context=context)
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True):
diff --git a/addons/membership/membership.py b/addons/membership/membership.py
index 6cc1f0e0f10..573c9026950 100644
--- a/addons/membership/membership.py
+++ b/addons/membership/membership.py
@@ -286,21 +286,21 @@ class Partner(osv.osv):
line_id = member_line_obj.search(cr, uid, [('partner', '=', partner_id),('date_cancel','=',False)],
limit=1, order='date_from', context=context)
if line_id:
- res[partner.id]['membership_start'] = member_line_obj.read(cr, uid, line_id[0],
- ['date_from'], context=context)['date_from']
+ res[partner.id]['membership_start'] = member_line_obj.read(cr, uid, [line_id[0]],
+ ['date_from'], context=context)[0]['date_from']
if name == 'membership_stop':
line_id1 = member_line_obj.search(cr, uid, [('partner', '=', partner_id),('date_cancel','=',False)],
limit=1, order='date_to desc', context=context)
if line_id1:
- res[partner.id]['membership_stop'] = member_line_obj.read(cr, uid, line_id1[0],
- ['date_to'], context=context)['date_to']
+ res[partner.id]['membership_stop'] = member_line_obj.read(cr, uid, [line_id1[0]],
+ ['date_to'], context=context)[0]['date_to']
if name == 'membership_cancel':
if partner.membership_state == 'canceled':
line_id2 = member_line_obj.search(cr, uid, [('partner', '=', partner.id)], limit=1, order='date_cancel', context=context)
if line_id2:
- res[partner.id]['membership_cancel'] = member_line_obj.read(cr, uid, line_id2[0], ['date_cancel'], context=context)['date_cancel']
+ res[partner.id]['membership_cancel'] = member_line_obj.read(cr, uid, [line_id2[0]], ['date_cancel'], context=context)[0]['date_cancel']
return res
def _get_partners(self, cr, uid, ids, context=None):
@@ -381,13 +381,6 @@ class Partner(osv.osv):
(_check_recursion, 'Error ! You cannot create recursive associated members.', ['associate_member'])
]
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default = default.copy()
- default['member_lines'] = []
- return super(Partner, self).copy(cr, uid, id, default, context=context)
-
def create_membership_invoice(self, cr, uid, ids, product_id=None, datas=None, context=None):
""" Create Customer Invoice of Membership for partners.
@param datas: datas has dictionary value which consist Id of Membership product and Cost Amount of Membership.
diff --git a/addons/membership/membership_demo.yml b/addons/membership/membership_demo.yml
index e21d70f0fd5..439666a4716 100644
--- a/addons/membership/membership_demo.yml
+++ b/addons/membership/membership_demo.yml
@@ -4,7 +4,7 @@
!python {model: res.partner}: |
invoice_ids = self.create_membership_invoice(cr, uid, [ref("base.res_partner_1"),ref("base.res_partner_14"),ref("base.res_partner_24"),ref("base.res_partner_19"),ref("base.res_partner_8"),ref("base.res_partner_5"),ref("base.res_partner_21"),ref("base.res_partner_6"),ref("base.res_partner_16"),ref("base.res_partner_10")], product_id=ref("membership_1"), datas={"amount":80.00})
invoice_pool = self.pool.get('account.invoice')
- invoice_pool.signal_invoice_open(cr, uid, invoice_ids)
+ invoice_pool.signal_workflow(cr, uid, invoice_ids, 'invoice_open')
for id in invoice_ids[-4:]:
pay = invoice_pool.pay_and_reconcile(cr, uid, [id],
diff --git a/addons/membership/test/test_membership.yml b/addons/membership/test/test_membership.yml
index 489b8b06afb..49c4f645877 100644
--- a/addons/membership/test/test_membership.yml
+++ b/addons/membership/test/test_membership.yml
@@ -40,7 +40,7 @@
membership_lines = membership_line_pool.browse(cr, uid, membership_line_ids)
assert membership_lines, 'Membership is not registrated.'
membership_line = membership_lines[0]
- invoice_pool.signal_invoice_open(cr, uid, [membership_line.account_invoice_id.id])
+ membership_line.account_invoice_id.signal_workflow('invoice_open')
- |
I'm checking "Current membership state" of "Seagate". It is an "Invoiced Member" or not.
diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py
index d102e6720a7..73d0688a543 100644
--- a/addons/mrp/mrp.py
+++ b/addons/mrp/mrp.py
@@ -108,7 +108,7 @@ class mrp_routing(osv.osv):
'code': fields.char('Code', size=8),
'note': fields.text('Description'),
- 'workcenter_lines': fields.one2many('mrp.routing.workcenter', 'routing_id', 'Work Centers'),
+ 'workcenter_lines': fields.one2many('mrp.routing.workcenter', 'routing_id', 'Work Centers', copy=True),
'location_id': fields.many2one('stock.location', 'Production Location',
help="Keep empty if you produce at the location where the finished products are needed." \
@@ -202,7 +202,7 @@ class mrp_bom(osv.osv):
'product_id': fields.many2one('product.product', 'Product Variant',
domain="[('product_tmpl_id','=',product_tmpl_id)]",
help="If a product variant is defined the BOM is available only for this product."),
- 'bom_line_ids': fields.one2many('mrp.bom.line', 'bom_id', 'BoM Lines'),
+ 'bom_line_ids': fields.one2many('mrp.bom.line', 'bom_id', 'BoM Lines', copy=True),
'product_qty': fields.float('Product Quantity', required=True, digits_compute=dp.get_precision('Product Unit of Measure')),
'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for the inventory control"),
'date_start': fields.date('Valid From', help="Validity of this BoM. Keep empty if it's always valid."),
@@ -510,9 +510,9 @@ class mrp_production(osv.osv):
return res
_columns = {
- 'name': fields.char('Reference', required=True, readonly=True, states={'draft': [('readonly', False)]}),
+ 'name': fields.char('Reference', required=True, readonly=True, states={'draft': [('readonly', False)]}, copy=False),
'origin': fields.char('Source Document', readonly=True, states={'draft': [('readonly', False)]},
- help="Reference of the document that generated this production order request."),
+ help="Reference of the document that generated this production order request.", copy=False),
'priority': fields.selection([('0', 'Not urgent'), ('1', 'Normal'), ('2', 'Urgent'), ('3', 'Very Urgent')], 'Priority',
select=True, readonly=True, states=dict.fromkeys(['draft', 'confirmed'], [('readonly', False)])),
@@ -530,14 +530,14 @@ class mrp_production(osv.osv):
'location_dest_id': fields.many2one('stock.location', 'Finished Products Location', required=True,
readonly=True, states={'draft': [('readonly', False)]},
help="Location where the system will stock the finished products."),
- 'date_planned': fields.datetime('Scheduled Date', required=True, select=1, readonly=True, states={'draft': [('readonly', False)]}),
- 'date_start': fields.datetime('Start Date', select=True, readonly=True),
- 'date_finished': fields.datetime('End Date', select=True, readonly=True),
+ 'date_planned': fields.datetime('Scheduled Date', required=True, select=1, readonly=True, states={'draft': [('readonly', False)]}, copy=False),
+ 'date_start': fields.datetime('Start Date', select=True, readonly=True, copy=False),
+ 'date_finished': fields.datetime('End Date', select=True, readonly=True, copy=False),
'bom_id': fields.many2one('mrp.bom', 'Bill of Material', readonly=True, states={'draft': [('readonly', False)]},
help="Bill of Materials allow you to define the list of required raw materials to make a finished product."),
'routing_id': fields.many2one('mrp.routing', string='Routing', on_delete='set null', readonly=True, states={'draft': [('readonly', False)]},
help="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."),
- 'move_prod_id': fields.many2one('stock.move', 'Product Move', readonly=True),
+ 'move_prod_id': fields.many2one('stock.move', 'Product Move', readonly=True, copy=False),
'move_lines': fields.one2many('stock.move', 'raw_material_production_id', 'Products to Consume',
domain=[('state', 'not in', ('done', 'cancel'))], readonly=True, states={'draft': [('readonly', False)]}),
'move_lines2': fields.one2many('stock.move', 'raw_material_production_id', 'Consumed Products',
@@ -554,7 +554,7 @@ class mrp_production(osv.osv):
[('draft', 'New'), ('cancel', 'Cancelled'), ('confirmed', 'Awaiting Raw Materials'),
('ready', 'Ready to Produce'), ('in_production', 'Production Started'), ('done', 'Done')],
string='Status', readonly=True,
- track_visibility='onchange',
+ track_visibility='onchange', copy=False,
help="When the production order is created the status is set to 'Draft'.\n\
If the order is confirmed the status is set to 'Waiting Goods'.\n\
If any exceptions are there, the status is set to 'Picking Exception'.\n\
@@ -602,20 +602,6 @@ class mrp_production(osv.osv):
raise osv.except_osv(_('Invalid Action!'), _('Cannot delete a manufacturing order in state \'%s\'.') % production.state)
return super(mrp_production, self).unlink(cr, uid, ids, context=context)
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default.update({
- 'name': self.pool.get('ir.sequence').get(cr, uid, 'mrp.production'),
- 'move_lines': [],
- 'move_lines2': [],
- 'move_created_ids': [],
- 'move_created_ids2': [],
- 'product_lines': [],
- 'move_prod_id': False,
- })
- return super(mrp_production, self).copy(cr, uid, id, default, context)
-
def location_id_change(self, cr, uid, ids, src, dest, context=None):
""" Changes destination location if source location is changed.
@param src: Source location id.
diff --git a/addons/mrp/product.py b/addons/mrp/product.py
index 2c3ae955f24..1b9873d0af7 100644
--- a/addons/mrp/product.py
+++ b/addons/mrp/product.py
@@ -51,14 +51,6 @@ class product_template(osv.osv):
_defaults = {
"produce_delay": 1,
}
-
- def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({
- 'bom_ids': []
- })
- return super(product_template, self).copy(cr, uid, id, default, context=context)
class product_product(osv.osv):
_inherit = "product.product"
diff --git a/addons/mrp/stock.py b/addons/mrp/stock.py
index 1ff7e7e7c8c..e04c434a741 100644
--- a/addons/mrp/stock.py
+++ b/addons/mrp/stock.py
@@ -31,17 +31,11 @@ class StockMove(osv.osv):
_inherit = 'stock.move'
_columns = {
- 'production_id': fields.many2one('mrp.production', 'Production Order for Produced Products', select=True),
+ 'production_id': fields.many2one('mrp.production', 'Production Order for Produced Products', select=True, copy=False),
'raw_material_production_id': fields.many2one('mrp.production', 'Production Order for Raw Materials', select=True),
'consumed_for': fields.many2one('stock.move', 'Consumed for', help='Technical field used to make the traceability of produced products'),
}
- def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default['production_id'] = False
- return super(StockMove, self).copy(cr, uid, id, default, context=context)
-
def check_tracking(self, cr, uid, move, lot_id, context=None):
super(StockMove, self).check_tracking(cr, uid, move, lot_id, context=context)
if move.product_id.track_production and (move.location_id.usage == 'production' or move.location_dest_id.usage == 'production') and not lot_id:
diff --git a/addons/mrp/tests/__init__.py b/addons/mrp/tests/__init__.py
deleted file mode 100644
index 39ebd5e451f..00000000000
--- a/addons/mrp/tests/__init__.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- 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 . import test_multicompany
-
-checks = [
- test_multicompany,
-]
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/mrp_byproduct/mrp_byproduct.py b/addons/mrp_byproduct/mrp_byproduct.py
index fc6069fc8ad..5649604b57d 100644
--- a/addons/mrp_byproduct/mrp_byproduct.py
+++ b/addons/mrp_byproduct/mrp_byproduct.py
@@ -71,7 +71,7 @@ class mrp_bom(osv.osv):
_inherit='mrp.bom'
_columns={
- 'sub_products':fields.one2many('mrp.subproduct', 'bom_id', 'Byproducts'),
+ 'sub_products':fields.one2many('mrp.subproduct', 'bom_id', 'Byproducts', copy=True),
}
diff --git a/addons/mrp_operations/mrp_operations.py b/addons/mrp_operations/mrp_operations.py
index f28a2d22ca9..c14b755070d 100644
--- a/addons/mrp_operations/mrp_operations.py
+++ b/addons/mrp_operations/mrp_operations.py
@@ -38,15 +38,6 @@ class stock_move(osv.osv):
'move_dest_id_lines': fields.one2many('stock.move','move_dest_id', 'Children Moves')
}
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default.update({
- 'move_dest_id_lines': [],
- })
- return super(stock_move, self).copy(cr, uid, id, default, context)
-
-
class mrp_production_workcenter_line(osv.osv):
def _get_date_end(self, cr, uid, ids, field_name, arg, context=None):
@@ -84,7 +75,7 @@ class mrp_production_workcenter_line(osv.osv):
_order = "sequence, date_planned"
_columns = {
- 'state': fields.selection([('draft','Draft'),('cancel','Cancelled'),('pause','Pending'),('startworking', 'In Progress'),('done','Finished')],'Status', readonly=True,
+ 'state': fields.selection([('draft','Draft'),('cancel','Cancelled'),('pause','Pending'),('startworking', 'In Progress'),('done','Finished')],'Status', readonly=True, copy=False,
help="* When a work order is created it is set in 'Draft' status.\n" \
"* When user sets work order in start mode that time it will be set in 'In Progress' status.\n" \
"* When work order is in running mode, during that time if user wants to stop or to make changes in order then can set in 'Pending' status.\n" \
@@ -120,27 +111,24 @@ class mrp_production_workcenter_line(osv.osv):
oper_obj = self.browse(cr, uid, ids)[0]
prod_obj = oper_obj.production_id
if action == 'start':
- if prod_obj.state =='confirmed':
- prod_obj_pool.force_production(cr, uid, [prod_obj.id])
- prod_obj_pool.signal_button_produce(cr, uid, [prod_obj.id])
- elif prod_obj.state =='ready':
- prod_obj_pool.signal_button_produce(cr, uid, [prod_obj.id])
- elif prod_obj.state =='in_production':
- return
- else:
- raise osv.except_osv(_('Error!'),_('Manufacturing order cannot be started in state "%s"!') % (prod_obj.state,))
+ if prod_obj.state =='confirmed':
+ prod_obj_pool.force_production(cr, uid, [prod_obj.id])
+ prod_obj_pool.signal_workflow(cr, uid, [prod_obj.id], 'button_produce')
+ elif prod_obj.state =='ready':
+ prod_obj_pool.signal_workflow(cr, uid, [prod_obj.id], 'button_produce')
+ elif prod_obj.state =='in_production':
+ return
+ else:
+ raise osv.except_osv(_('Error!'),_('Manufacturing order cannot be started in state "%s"!') % (prod_obj.state,))
else:
- oper_ids = self.search(cr,uid,[('production_id','=',prod_obj.id)])
- obj = self.browse(cr,uid,oper_ids)
- flag = True
- for line in obj:
- if line.state != 'done':
- flag = False
+ open_count = self.search_count(cr,uid,[('production_id','=',prod_obj.id), ('state', '!=', 'done')])
+ flag = not bool(open_count)
+
if flag:
for production in prod_obj_pool.browse(cr, uid, [prod_obj.id], context= None):
if production.move_lines or production.move_created_ids:
prod_obj_pool.action_produce(cr,uid, production.id, production.product_qty, 'consume_produce', context = None)
- prod_obj_pool.signal_button_produce_done(cr, uid, [oper_obj.production_id.id])
+ prod_obj_pool.signal_workflow(cr, uid, [oper_obj.production_id.id], 'button_produce_done')
return
def write(self, cr, uid, ids, vals, context=None, update=True):
@@ -228,8 +216,8 @@ class mrp_production(osv.osv):
workcenter_pool = self.pool.get('mrp.production.workcenter.line')
for workcenter_line in obj.workcenter_lines:
if workcenter_line.state == 'draft':
- workcenter_pool.signal_button_start_working(cr, uid, [workcenter_line.id])
- workcenter_pool.signal_button_done(cr, uid, [workcenter_line.id])
+ workcenter_line.signal_workflow('button_start_working')
+ workcenter_line.signal_workflow('button_done')
return super(mrp_production,self).action_production_end(cr, uid, ids)
def action_in_production(self, cr, uid, ids):
@@ -239,7 +227,7 @@ class mrp_production(osv.osv):
workcenter_pool = self.pool.get('mrp.production.workcenter.line')
for prod in self.browse(cr, uid, ids):
if prod.workcenter_lines:
- workcenter_pool.signal_button_start_working(cr, uid, [prod.workcenter_lines[0].id])
+ workcenter_pool.signal_workflow(cr, uid, [prod.workcenter_lines[0].id], 'button_start_working')
return super(mrp_production,self).action_in_production(cr, uid, ids)
def action_cancel(self, cr, uid, ids, context=None):
@@ -248,8 +236,7 @@ class mrp_production(osv.osv):
"""
workcenter_pool = self.pool.get('mrp.production.workcenter.line')
obj = self.browse(cr, uid, ids,context=context)[0]
- for workcenter_line in obj.workcenter_lines:
- workcenter_pool.signal_button_cancel(cr, uid, [workcenter_line.id])
+ workcenter_pool.signal_workflow(cr, uid, [record.id for record in obj.workcenter_lines], 'button_cancel')
return super(mrp_production,self).action_cancel(cr,uid,ids,context=context)
def _compute_planned_workcenter(self, cr, uid, ids, context=None, mini=False):
@@ -507,24 +494,24 @@ class mrp_operations_operation(osv.osv):
wc_op_id.append(workcenter_pool.create(cr,uid,{'production_id':vals['production_id'],'name':production_obj.product_id.name,'workcenter_id':vals['workcenter_id']}))
if code.start_stop=='start':
workcenter_pool.action_start_working(cr,uid,wc_op_id)
- workcenter_pool.signal_button_start_working(cr, uid, [wc_op_id[0]])
+ workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]], 'button_start_working')
if code.start_stop=='done':
workcenter_pool.action_done(cr,uid,wc_op_id)
- workcenter_pool.signal_button_done(cr, uid, [wc_op_id[0]])
+ workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]], 'button_done')
self.pool.get('mrp.production').write(cr,uid,vals['production_id'],{'date_finished':datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
if code.start_stop=='pause':
workcenter_pool.action_pause(cr,uid,wc_op_id)
- workcenter_pool.signal_button_pause(cr, uid, [wc_op_id[0]])
+ workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]], 'button_pause')
if code.start_stop=='resume':
workcenter_pool.action_resume(cr,uid,wc_op_id)
- workcenter_pool.signal_button_resume(cr, uid, [wc_op_id[0]])
+ workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]], 'button_resume')
if code.start_stop=='cancel':
workcenter_pool.action_cancel(cr,uid,wc_op_id)
- workcenter_pool.signal_button_cancel(cr, uid, [wc_op_id[0]])
+ workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]], 'button_cancel')
if not self.check_operation(cr, uid, vals):
return
@@ -560,4 +547,3 @@ class mrp_operations_operation(osv.osv):
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
diff --git a/addons/mrp_operations/test/workcenter_operations.yml b/addons/mrp_operations/test/workcenter_operations.yml
index 6e9beafa156..a31c3fb05d2 100644
--- a/addons/mrp_operations/test/workcenter_operations.yml
+++ b/addons/mrp_operations/test/workcenter_operations.yml
@@ -58,47 +58,47 @@
-
!python {model: mrp.production}: |
order = self.browse(cr, uid, ref("mrp.mrp_production_1"), context=context)
- self.pool.get('mrp.production.workcenter.line').signal_button_start_working(cr, uid, [order.workcenter_lines[0].id])
+ order.workcenter_lines[0].signal_workflow('button_start_working')
-
Now I pause first work operation due to technical fault of work center.
-
!python {model: mrp.production}: |
order = self.browse(cr, uid, ref("mrp.mrp_production_1"), context=context)
- self.pool.get('mrp.production.workcenter.line').signal_button_pause(cr, uid, [order.workcenter_lines[0].id])
+ order.workcenter_lines[0].signal_workflow('button_pause')
-
I resume first work operation.
-
!python {model: mrp.production}: |
order = self.browse(cr, uid, ref("mrp.mrp_production_1"), context=context)
- self.pool.get('mrp.production.workcenter.line').signal_button_resume(cr, uid, [order.workcenter_lines[0].id])
+ order.workcenter_lines[0].signal_workflow('button_resume')
-
I cancel first work operation.
-
!python {model: mrp.production}: |
order = self.browse(cr, uid, ref("mrp.mrp_production_1"), context=context)
- self.pool.get('mrp.production.workcenter.line').signal_button_cancel(cr, uid, [order.workcenter_lines[0].id])
+ order.workcenter_lines[0].signal_workflow('button_cancel')
-
I reset first work operation and start after resolving techninal fault of work center.
-
!python {model: mrp.production}: |
order = self.browse(cr, uid, ref("mrp.mrp_production_1"), context=context)
- self.pool.get('mrp.production.workcenter.line').signal_button_draft(cr, uid, [order.workcenter_lines[0].id])
- self.pool.get('mrp.production.workcenter.line').signal_button_start_working(cr, uid, [order.workcenter_lines[0].id])
+ order.workcenter_lines[0].signal_workflow('button_draft')
+ order.workcenter_lines[0].signal_workflow('button_start_working')
-
I close first work operation as this work center completed its process.
-
!python {model: mrp.production}: |
order = self.browse(cr, uid, ref("mrp.mrp_production_1"), context=context)
- self.pool.get('mrp.production.workcenter.line').signal_button_done(cr, uid, [order.workcenter_lines[0].id])
+ order.workcenter_lines[0].signal_workflow('button_done')
-
Now I close other operations one by one which are in start state.
-
!python {model: mrp.production}: |
order = self.browse(cr, uid, ref("mrp.mrp_production_1"), context=context)
for work_line in order.workcenter_lines[1:]:
- self.pool.get('mrp.production.workcenter.line').signal_button_start_working(cr, uid, [work_line.id])
- self.pool.get('mrp.production.workcenter.line').signal_button_done(cr, uid, [work_line.id])
+ work_line.signal_workflow('button_start_working')
+ work_line.signal_workflow('button_done')
-
I check that the production order is now done.
diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py
index 6ff39b79081..a349c82ba2d 100644
--- a/addons/mrp_repair/mrp_repair.py
+++ b/addons/mrp_repair/mrp_repair.py
@@ -113,7 +113,7 @@ class mrp_repair(osv.osv):
return self.pool['mrp.repair'].search(cr, uid, [('fees_lines', 'in', ids)], context=context)
_columns = {
- 'name': fields.char('Repair Reference', required=True, states={'confirmed': [('readonly', True)]}),
+ 'name': fields.char('Repair Reference', required=True, states={'confirmed': [('readonly', True)]}, copy=False),
'product_id': fields.many2one('product.product', string='Product to Repair', required=True, readonly=True, states={'draft': [('readonly', False)]}),
'product_qty': fields.float('Product Quantity', digits_compute=dp.get_precision('Product Unit of Measure'),
required=True, readonly=True, states={'draft': [('readonly', False)]}),
@@ -130,7 +130,7 @@ class mrp_repair(osv.osv):
('2binvoiced', 'To be Invoiced'),
('invoice_except', 'Invoice Exception'),
('done', 'Repaired')
- ], 'Status', readonly=True, track_visibility='onchange',
+ ], 'Status', readonly=True, track_visibility='onchange', copy=False,
help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed repair order. \
\n* The \'Confirmed\' status is used when a user confirms the repair order. \
\n* The \'Ready to Repair\' status is used to start to repairing, user can start repairing only after repair order is confirmed. \
@@ -141,7 +141,7 @@ class mrp_repair(osv.osv):
'location_dest_id': fields.many2one('stock.location', 'Delivery Location', readonly=True, required=True, states={'draft': [('readonly', False)], 'confirmed': [('readonly', True)]}),
'lot_id': fields.many2one('stock.production.lot', 'Repaired Lot', domain="[('product_id','=', product_id)]", help="Products repaired are all belonging to this lot"),
'guarantee_limit': fields.date('Warranty Expiration', help="The warranty expiration limit is computed as: last move date + warranty defined on selected product. If the current date is below the warranty expiration limit, each operation and fee you will add will be set as 'not to invoiced' by default. Note that you can change manually afterwards.", states={'confirmed': [('readonly', True)]}),
- 'operations': fields.one2many('mrp.repair.line', 'repair_id', 'Operation Lines', readonly=True, states={'draft': [('readonly', False)]}),
+ 'operations': fields.one2many('mrp.repair.line', 'repair_id', 'Operation Lines', readonly=True, states={'draft': [('readonly', False)]}, copy=True),
'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', help='Pricelist of the selected partner.'),
'partner_invoice_id': fields.many2one('res.partner', 'Invoicing Address'),
'invoice_method': fields.selection([
@@ -150,14 +150,14 @@ class mrp_repair(osv.osv):
("after_repair", "After Repair")
], "Invoice Method",
select=True, required=True, states={'draft': [('readonly', False)]}, readonly=True, help='Selecting \'Before Repair\' or \'After Repair\' will allow you to generate invoice before or after the repair is done respectively. \'No invoice\' means you don\'t want to generate invoice for this repair order.'),
- 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True, track_visibility="onchange"),
- 'move_id': fields.many2one('stock.move', 'Move', readonly=True, help="Move created by the repair order", track_visibility="onchange"),
- 'fees_lines': fields.one2many('mrp.repair.fee', 'repair_id', 'Fees', readonly=True, states={'draft': [('readonly', False)]}),
+ 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True, track_visibility="onchange", copy=False),
+ 'move_id': fields.many2one('stock.move', 'Move', readonly=True, help="Move created by the repair order", track_visibility="onchange", copy=False),
+ 'fees_lines': fields.one2many('mrp.repair.fee', 'repair_id', 'Fees', readonly=True, states={'draft': [('readonly', False)]}, copy=True),
'internal_notes': fields.text('Internal Notes'),
'quotation_notes': fields.text('Quotation Notes'),
'company_id': fields.many2one('res.company', 'Company'),
- 'invoiced': fields.boolean('Invoiced', readonly=True),
- 'repaired': fields.boolean('Repaired', readonly=True),
+ 'invoiced': fields.boolean('Invoiced', readonly=True, copy=False),
+ 'repaired': fields.boolean('Repaired', readonly=True, copy=False),
'amount_untaxed': fields.function(_amount_untaxed, string='Untaxed Amount',
store={
'mrp.repair': (lambda self, cr, uid, ids, c={}: ids, ['operations', 'fees_lines'], 10),
@@ -199,19 +199,6 @@ class mrp_repair(osv.osv):
('name', 'unique (name)', 'The name of the Repair Order must be unique!'),
]
- def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({
- 'state': 'draft',
- 'repaired': False,
- 'invoiced': False,
- 'invoice_id': False,
- 'move_id': False,
- 'name': self.pool.get('ir.sequence').get(cr, uid, 'mrp.repair'),
- })
- return super(mrp_repair, self).copy(cr, uid, id, default, context)
-
def onchange_product_id(self, cr, uid, ids, product_id=None):
""" On change of product sets some values.
@param product_id: Changed product
@@ -562,12 +549,6 @@ class mrp_repair_line(osv.osv, ProductChangeMixin):
_name = 'mrp.repair.line'
_description = 'Repair Line'
- def copy_data(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({'invoice_line_id': False, 'move_id': False, 'invoiced': False, 'state': 'draft'})
- return super(mrp_repair_line, self).copy_data(cr, uid, id, default, context)
-
def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
""" Calculates amount.
@param field_name: Name of field.
@@ -588,22 +569,22 @@ class mrp_repair_line(osv.osv, ProductChangeMixin):
'type': fields.selection([('add', 'Add'), ('remove', 'Remove')], 'Type', required=True),
'to_invoice': fields.boolean('To Invoice'),
'product_id': fields.many2one('product.product', 'Product', required=True),
- 'invoiced': fields.boolean('Invoiced', readonly=True),
+ 'invoiced': fields.boolean('Invoiced', readonly=True, copy=False),
'price_unit': fields.float('Unit Price', required=True, digits_compute=dp.get_precision('Product Price')),
'price_subtotal': fields.function(_amount_line, string='Subtotal', digits_compute=dp.get_precision('Account')),
'tax_id': fields.many2many('account.tax', 'repair_operation_line_tax', 'repair_operation_line_id', 'tax_id', 'Taxes'),
'product_uom_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True),
- 'invoice_line_id': fields.many2one('account.invoice.line', 'Invoice Line', readonly=True),
+ 'invoice_line_id': fields.many2one('account.invoice.line', 'Invoice Line', readonly=True, copy=False),
'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True),
'location_dest_id': fields.many2one('stock.location', 'Dest. Location', required=True, select=True),
- 'move_id': fields.many2one('stock.move', 'Inventory Move', readonly=True),
+ 'move_id': fields.many2one('stock.move', 'Inventory Move', readonly=True, copy=False),
'lot_id': fields.many2one('stock.production.lot', 'Lot'),
'state': fields.selection([
('draft', 'Draft'),
('confirmed', 'Confirmed'),
('done', 'Done'),
- ('cancel', 'Cancelled')], 'Status', required=True, readonly=True,
+ ('cancel', 'Cancelled')], 'Status', required=True, readonly=True, copy=False,
help=' * The \'Draft\' status is set automatically as draft when repair order in draft status. \
\n* The \'Confirmed\' status is set automatically as confirm when repair order in confirm status. \
\n* The \'Done\' status is set automatically when repair order is completed.\
@@ -659,12 +640,6 @@ class mrp_repair_fee(osv.osv, ProductChangeMixin):
_name = 'mrp.repair.fee'
_description = 'Repair Fees Line'
- def copy_data(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({'invoice_line_id': False, 'invoiced': False})
- return super(mrp_repair_fee, self).copy_data(cr, uid, id, default, context)
-
def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
""" Calculates amount.
@param field_name: Name of field.
@@ -688,9 +663,9 @@ class mrp_repair_fee(osv.osv, ProductChangeMixin):
'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True),
'price_subtotal': fields.function(_amount_line, string='Subtotal', digits_compute=dp.get_precision('Account')),
'tax_id': fields.many2many('account.tax', 'repair_fee_line_tax', 'repair_fee_line_id', 'tax_id', 'Taxes'),
- 'invoice_line_id': fields.many2one('account.invoice.line', 'Invoice Line', readonly=True),
+ 'invoice_line_id': fields.many2one('account.invoice.line', 'Invoice Line', readonly=True, copy=False),
'to_invoice': fields.boolean('To Invoice'),
- 'invoiced': fields.boolean('Invoiced', readonly=True),
+ 'invoiced': fields.boolean('Invoiced', readonly=True, copy=False),
}
_defaults = {
diff --git a/addons/mrp_repair/security/ir.model.access.csv b/addons/mrp_repair/security/ir.model.access.csv
index 2ffbc89cf8d..c6f026ac356 100644
--- a/addons/mrp_repair/security/ir.model.access.csv
+++ b/addons/mrp_repair/security/ir.model.access.csv
@@ -23,5 +23,6 @@ access_account_invoice_user,account.invoice,account.model_account_invoice,mrp.gr
access_account_invoice_manager,account.invoice manager,account.model_account_invoice,mrp.group_mrp_manager,1,0,0,0
access_account_invoice_line_user,account.invoice.line,account.model_account_invoice_line,mrp.group_mrp_user,1,1,1,1
access_account_invoice_line_manager,account.invoice.line manager,account.model_account_invoice_line,mrp.group_mrp_manager,1,0,0,0
+access_account_invoice_tax_user,account.invoice.tax,account.model_account_invoice_tax,mrp.group_mrp_user,1,0,0,0
access_account_journal_user,account.journal,account.model_account_journal,mrp.group_mrp_user,1,1,1,1
access_account_journal_manager,account.journal manager,account.model_account_journal,mrp.group_mrp_manager,1,0,0,0
diff --git a/addons/mrp_repair/wizard/make_invoice.py b/addons/mrp_repair/wizard/make_invoice.py
index f9c84cb7bdb..e23d6a549a0 100644
--- a/addons/mrp_repair/wizard/make_invoice.py
+++ b/addons/mrp_repair/wizard/make_invoice.py
@@ -49,7 +49,7 @@ class make_invoice(osv.osv_memory):
# We have to trigger the workflow of the given repairs, otherwise they remain 'to be invoiced'.
# Note that the signal 'action_invoice_create' will trigger another call to the method 'action_invoice_create',
# but that second call will not do anything, since the repairs are already invoiced.
- order_obj.signal_action_invoice_create(cr, uid, context['active_ids'])
+ order_obj.signal_workflow(cr, uid, context['active_ids'], 'action_invoice_create')
form_res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_form')
form_id = form_res and form_res[1] or False
diff --git a/addons/multi_company/multi_company_demo.xml b/addons/multi_company/multi_company_demo.xml
index 3dad9fcaaf6..d532c3e3e91 100644
--- a/addons/multi_company/multi_company_demo.xml
+++ b/addons/multi_company/multi_company_demo.xml
@@ -397,10 +397,19 @@
+
+
+
+
+
+
diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py
index 2d466219140..25ff5fd5194 100644
--- a/addons/payment/models/payment_acquirer.py
+++ b/addons/payment/models/payment_acquirer.py
@@ -73,7 +73,7 @@ class PaymentAcquirer(osv.Model):
[('test', 'Test'), ('prod', 'Production')],
string='Environment', oldname='env'),
'website_published': fields.boolean(
- 'Visible in Portal / Website',
+ 'Visible in Portal / Website', copy=False,
help="Make this payment acquirer available (Customer invoices, etc.)"),
# Fees
'fees_active': fields.boolean('Compute fees'),
@@ -338,7 +338,7 @@ class PaymentTransaction(osv.Model):
('done', 'Done'), ('error', 'Error'),
('cancel', 'Canceled')
], 'Status', required=True,
- track_visiblity='onchange'),
+ track_visiblity='onchange', copy=False),
'state_message': fields.text('Message',
help='Field used to store error and/or validation messages for information'),
# payment
diff --git a/addons/point_of_sale/account_bank_statement.py b/addons/point_of_sale/account_bank_statement.py
index 9275b975d46..e698041f975 100644
--- a/addons/point_of_sale/account_bank_statement.py
+++ b/addons/point_of_sale/account_bank_statement.py
@@ -38,7 +38,7 @@ class account_journal(osv.osv):
class account_cash_statement(osv.osv):
_inherit = 'account.bank.statement'
_columns = {
- 'pos_session_id' : fields.many2one('pos.session'),
+ 'pos_session_id' : fields.many2one('pos.session', copy=False),
}
diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py
index 6bef9f77248..1eec3cb48d4 100644
--- a/addons/point_of_sale/point_of_sale.py
+++ b/addons/point_of_sale/point_of_sale.py
@@ -19,14 +19,9 @@
#
##############################################################################
-from datetime import datetime
-from dateutil.relativedelta import relativedelta
-from decimal import Decimal
import logging
-import pdb
import time
-import openerp
from openerp import tools
from openerp.osv import fields, osv
from openerp.tools.translate import _
@@ -81,10 +76,10 @@ class pos_config(osv.osv):
'receipt_footer': fields.text('Receipt Footer',help="A short text that will be inserted as a footer in the printed receipt"),
'proxy_ip': fields.char('IP Address', help='The hostname or ip address of the hardware proxy, Will be autodetected if left empty', size=45),
- 'state' : fields.selection(POS_CONFIG_STATE, 'Status', required=True, readonly=True),
+ 'state' : fields.selection(POS_CONFIG_STATE, 'Status', required=True, readonly=True, copy=False),
'sequence_id' : fields.many2one('ir.sequence', 'Order IDs Sequence', readonly=True,
help="This sequence is automatically created by OpenERP but you can change it "\
- "to customize the reference numbers of your orders."),
+ "to customize the reference numbers of your orders.", copy=False),
'session_ids': fields.one2many('pos.session', 'config_id', 'Sessions'),
'group_by' : fields.boolean('Group Journal Items', help="Check this if you want to group the Journal Items by Product while closing a Session"),
'pricelist_id': fields.many2one('product.pricelist','Pricelist', required=True),
@@ -107,16 +102,6 @@ class pos_config(osv.osv):
(_check_cash_control, "You cannot have two cash controls in one Point Of Sale !", ['journal_ids']),
]
- def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- d = {
- 'sequence_id' : False,
- }
- d.update(default)
- return super(pos_config, self).copy(cr, uid, id, d, context=context)
-
-
def name_get(self, cr, uid, ids, context=None):
result = []
states = {
@@ -252,7 +237,7 @@ class pos_session(osv.osv):
'state' : fields.selection(POS_SESSION_STATE, 'Status',
required=True, readonly=True,
- select=1),
+ select=1, copy=False),
'sequence_number': fields.integer('Order Sequence Number'),
@@ -353,7 +338,7 @@ class pos_session(osv.osv):
]
def create(self, cr, uid, values, context=None):
- context = context or {}
+ context = dict(context or {})
config_id = values.get('config_id', False) or context.get('default_config_id', False)
if not config_id:
raise osv.except_osv( _('Error!'),
@@ -440,9 +425,9 @@ class pos_session(osv.osv):
if not record.start_at:
values['start_at'] = time.strftime('%Y-%m-%d %H:%M:%S')
values['state'] = 'opened'
- record.write(values, context=context)
+ record.write(values)
for st in record.statement_ids:
- st.button_open(context=context)
+ st.button_open()
return self.open_frontend_cb(cr, uid, ids, context=context)
@@ -658,23 +643,8 @@ class pos_order(osv.osv):
res[order.id]['amount_total'] = cur_obj.round(cr, uid, cur, val1)
return res
- def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- d = {
- 'state': 'draft',
- 'invoice_id': False,
- 'account_move': False,
- 'picking_id': False,
- 'statement_ids': [],
- 'nb_print': 0,
- 'name': self.pool.get('ir.sequence').get(cr, uid, 'pos.order'),
- }
- d.update(default)
- return super(pos_order, self).copy(cr, uid, id, d, context=context)
-
_columns = {
- 'name': fields.char('Order Ref', required=True, readonly=True),
+ 'name': fields.char('Order Ref', required=True, readonly=True, copy=False),
'company_id':fields.many2one('res.company', 'Company', required=True, readonly=True),
'date_order': fields.datetime('Order Date', readonly=True, select=True),
'user_id': fields.many2one('res.users', 'Salesman', help="Person who uses the the cash register. It can be a reliever, a student or an interim employee."),
@@ -682,7 +652,7 @@ class pos_order(osv.osv):
'amount_total': fields.function(_amount_all, string='Total', multi='all'),
'amount_paid': fields.function(_amount_all, string='Paid', states={'draft': [('readonly', False)]}, readonly=True, digits_compute=dp.get_precision('Account'), multi='all'),
'amount_return': fields.function(_amount_all, 'Returned', digits_compute=dp.get_precision('Account'), multi='all'),
- 'lines': fields.one2many('pos.order.line', 'order_id', 'Order Lines', states={'draft': [('readonly', False)]}, readonly=True),
+ 'lines': fields.one2many('pos.order.line', 'order_id', 'Order Lines', states={'draft': [('readonly', False)]}, readonly=True, copy=True),
'statement_ids': fields.one2many('account.bank.statement.line', 'pos_statement_id', 'Payments', states={'draft': [('readonly', False)]}, readonly=True),
'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True, states={'draft': [('readonly', False)]}, readonly=True),
'partner_id': fields.many2one('res.partner', 'Customer', change_default=True, select=1, states={'draft': [('readonly', False)], 'paid': [('readonly', False)]}),
@@ -700,16 +670,16 @@ class pos_order(osv.osv):
('paid', 'Paid'),
('done', 'Posted'),
('invoiced', 'Invoiced')],
- 'Status', readonly=True),
+ 'Status', readonly=True, copy=False),
- 'invoice_id': fields.many2one('account.invoice', 'Invoice'),
- 'account_move': fields.many2one('account.move', 'Journal Entry', readonly=True),
- 'picking_id': fields.many2one('stock.picking', 'Picking', readonly=True),
+ 'invoice_id': fields.many2one('account.invoice', 'Invoice', copy=False),
+ 'account_move': fields.many2one('account.move', 'Journal Entry', readonly=True, copy=False),
+ 'picking_id': fields.many2one('stock.picking', 'Picking', readonly=True, copy=False),
'picking_type_id': fields.related('session_id', 'config_id', 'picking_type_id', string="Picking Type", type='many2one', relation='stock.picking.type'),
'location_id': fields.related('session_id', 'config_id', 'stock_location_id', string="Location", type='many2one', store=True, relation='stock.location'),
'note': fields.text('Internal Notes'),
- 'nb_print': fields.integer('Number of Print', readonly=True),
- 'pos_reference': fields.char('Receipt Ref', readonly=True),
+ 'nb_print': fields.integer('Number of Print', readonly=True, copy=False),
+ 'pos_reference': fields.char('Receipt Ref', readonly=True, copy=False),
'sale_journal': fields.related('session_id', 'config_id', 'journal_id', relation='account.journal', type='many2one', string='Sale Journal', store=True, readonly=True),
}
@@ -831,8 +801,7 @@ class pos_order(osv.osv):
def add_payment(self, cr, uid, order_id, data, context=None):
"""Create a new payment for the order"""
- if not context:
- context = {}
+ context = dict(context or {})
statement_line_obj = self.pool.get('account.bank.statement.line')
property_obj = self.pool.get('ir.property')
order = self.browse(cr, uid, order_id, context=context)
@@ -1282,7 +1251,7 @@ class pos_order_line(osv.osv):
_columns = {
'company_id': fields.many2one('res.company', 'Company', required=True),
- 'name': fields.char('Line No', required=True),
+ 'name': fields.char('Line No', required=True, copy=False),
'notice': fields.char('Discount Notice'),
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], required=True, change_default=True),
'price_unit': fields.float(string='Unit Price', digits_compute=dp.get_precision('Account')),
@@ -1301,16 +1270,6 @@ class pos_order_line(osv.osv):
'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id,
}
- def copy_data(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({
- 'name': self.pool.get('ir.sequence').get(cr, uid, 'pos.order.line')
- })
- return super(pos_order_line, self).copy_data(cr, uid, id, default, context=context)
-
-import io, StringIO
-
class ean_wizard(osv.osv_memory):
_name = 'pos.ean_wizard'
_columns = {
diff --git a/addons/point_of_sale/test/01_order_to_payment.yml b/addons/point_of_sale/test/01_order_to_payment.yml
index bf201affc4a..cdad0d1f309 100644
--- a/addons/point_of_sale/test/01_order_to_payment.yml
+++ b/addons/point_of_sale/test/01_order_to_payment.yml
@@ -14,7 +14,7 @@
-
I assign this 10 percent tax on the [PCSC234] PC Assemble SC234 product as a sale tax
-
- !record {model: product.product, id: product.product_product_3}:
+ !record {model: product.product, id: product.product_product_3, view: False}:
taxes_id: [account_tax_10_incl]
- |
I create a VAT tax of 5%, which is added to the public price
@@ -42,7 +42,7 @@
-
I assign those 5 percent taxes on the PCSC349 product as a sale taxes
-
- !record {model: product.product, id: product.product_product_4}:
+ !record {model: product.product, id: product.product_product_4, view: False}:
taxes_id: [account_tax_05_incl, account_tax_05_incl_chicago]
-
I create a new session
diff --git a/addons/point_of_sale/wizard/pos_confirm.py b/addons/point_of_sale/wizard/pos_confirm.py
index d1ecdfb4e0e..909463fa996 100644
--- a/addons/point_of_sale/wizard/pos_confirm.py
+++ b/addons/point_of_sale/wizard/pos_confirm.py
@@ -36,7 +36,7 @@ class pos_confirm(osv.osv_memory):
todo = False
break
if todo:
- order_obj.signal_done(cr, uid, [order.id])
+ order.signal_workflow('done')
# Check if there is orders to reconcile their invoices
ids = order_obj.search(cr, uid, [('state','=','invoiced'),('invoice_id.state','=','open')], context=context)
diff --git a/addons/point_of_sale/wizard/pos_payment.py b/addons/point_of_sale/wizard/pos_payment.py
index 567d5e57f94..efd2682bc30 100644
--- a/addons/point_of_sale/wizard/pos_payment.py
+++ b/addons/point_of_sale/wizard/pos_payment.py
@@ -65,9 +65,8 @@ class pos_make_payment(osv.osv_memory):
order_obj.add_payment(cr, uid, active_id, data, context=context)
if order_obj.test_paid(cr, uid, [active_id]):
- order_obj.signal_paid(cr, uid, [active_id])
+ order_obj.signal_workflow(cr, uid, [active_id], 'paid')
return {'type' : 'ir.actions.act_window_close' }
- ##self.print_report(cr, uid, ids, context=context)
return self.launch_payment(cr, uid, ids, context=context)
diff --git a/addons/point_of_sale/wizard/pos_session_opening.py b/addons/point_of_sale/wizard/pos_session_opening.py
index ea717378a3b..5389fb3e1a0 100644
--- a/addons/point_of_sale/wizard/pos_session_opening.py
+++ b/addons/point_of_sale/wizard/pos_session_opening.py
@@ -24,8 +24,8 @@ class pos_session_opening(osv.osv_memory):
}
def open_ui(self, cr, uid, ids, context=None):
- context = context or {}
data = self.browse(cr, uid, ids[0], context=context)
+ context = dict(context or {})
context['active_id'] = data.pos_session_id.id
return {
'type' : 'ir.actions.act_url',
@@ -35,7 +35,7 @@ class pos_session_opening(osv.osv_memory):
def open_existing_session_cb_close(self, cr, uid, ids, context=None):
wizard = self.browse(cr, uid, ids[0], context=context)
- self.pool.get('pos.session').signal_cashbox_control(cr, uid, [wizard.pos_session_id.id])
+ wizard.pos_session_id.signal_workflow('cashbox_control')
return self.open_session_cb(cr, uid, ids, context)
def open_session_cb(self, cr, uid, ids, context=None):
diff --git a/addons/portal/tests/test_portal.py b/addons/portal/tests/test_portal.py
index 2c4d7ecf966..04122289316 100644
--- a/addons/portal/tests/test_portal.py
+++ b/addons/portal/tests/test_portal.py
@@ -48,7 +48,7 @@ class test_portal(TestMail):
# Set an email address for the user running the tests, used as Sender for outgoing mails
self.res_users.write(cr, uid, uid, {'email': 'test@localhost'})
- @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
+ @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
def test_00_mail_access_rights(self):
""" Test basic mail_message and mail_group access rights for portal users. """
cr, uid = self.cr, self.uid
@@ -90,7 +90,7 @@ class test_portal(TestMail):
# Do: Chell replies to a Pigs message using the composer
compose_id = mail_compose.create(cr, self.user_chell_id,
{'subject': 'Subject', 'body': 'Body text'},
- {'default_composition_mode': 'reply', 'default_parent_id': pigs_msg_id})
+ {'default_composition_mode': 'comment', 'default_parent_id': pigs_msg_id})
mail_compose.send_mail(cr, self.user_chell_id, [compose_id])
# Do: Chell browses PigsPortal -> ok because groups security, ko for partners (no read permission)
@@ -167,7 +167,7 @@ class test_portal(TestMail):
self.assertIn('login=%s' % partner_raoul.user_ids[0].login, url,
'notification email: link should contain the user login')
- @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm')
+ @mute_logger('openerp.addons.mail.mail_thread', 'openerp.models')
def test_21_inbox_redirection(self):
""" Tests designed to test the inbox redirection of emails notification URLs. """
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
diff --git a/addons/portal_project/tests/test_access_rights.py b/addons/portal_project/tests/test_access_rights.py
index 0cb2412ecea..e36df90b652 100644
--- a/addons/portal_project/tests/test_access_rights.py
+++ b/addons/portal_project/tests/test_access_rights.py
@@ -78,7 +78,7 @@ class TestPortalProjectBase(TestProjectBase):
class TestPortalProject(TestPortalProjectBase):
- @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
+ @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
def test_00_project_access_rights(self):
""" Test basic project access rights, for project and portal_project """
cr, uid, pigs_id = self.cr, self.uid, self.project_pigs_id
@@ -88,7 +88,7 @@ class TestPortalProject(TestPortalProjectBase):
# ----------------------------------------
# Do: Alfred reads project -> ok (employee ok public)
- self.project_project.read(cr, self.user_projectuser_id, pigs_id, ['name'])
+ self.project_project.read(cr, self.user_projectuser_id, [pigs_id], ['state'])
# Test: all project tasks visible
task_ids = self.project_task.search(cr, self.user_projectuser_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_1_id, self.task_2_id, self.task_3_id, self.task_4_id, self.task_5_id, self.task_6_id])
@@ -100,7 +100,7 @@ class TestPortalProject(TestPortalProjectBase):
self.project_task.write(cr, self.user_projectuser_id, task_ids, {'description': 'TestDescription'})
# Do: Bert reads project -> crash, no group
- self.assertRaises(AccessError, self.project_project.read, cr, self.user_none_id, pigs_id, ['name'])
+ self.assertRaises(AccessError, self.project_project.read, cr, self.user_none_id, [pigs_id], ['state'])
# Test: no project task visible
self.assertRaises(AccessError, self.project_task.search, cr, self.user_none_id, [('project_id', '=', pigs_id)])
# Test: no project task readable
@@ -109,7 +109,7 @@ class TestPortalProject(TestPortalProjectBase):
self.assertRaises(AccessError, self.project_task.write, cr, self.user_none_id, task_ids, {'description': 'TestDescription'})
# Do: Chell reads project -> ok (portal ok public)
- self.project_project.read(cr, self.user_portal_id, pigs_id, ['name'])
+ self.project_project.read(cr, self.user_portal_id, [pigs_id], ['state'])
# Test: all project tasks visible
task_ids = self.project_task.search(cr, self.user_portal_id, [('project_id', '=', pigs_id)])
self.assertEqual(set(task_ids), test_task_ids,
@@ -120,7 +120,7 @@ class TestPortalProject(TestPortalProjectBase):
self.assertRaises(AccessError, self.project_task.write, cr, self.user_portal_id, task_ids, {'description': 'TestDescription'})
# Do: Donovan reads project -> ok (public)
- self.project_project.read(cr, self.user_public_id, pigs_id, ['name'])
+ self.project_project.read(cr, self.user_public_id, [pigs_id], ['state'])
# Test: all project tasks visible
task_ids = self.project_task.search(cr, self.user_public_id, [('project_id', '=', pigs_id)])
self.assertEqual(set(task_ids), test_task_ids,
@@ -134,16 +134,17 @@ class TestPortalProject(TestPortalProjectBase):
# CASE2: portal project
# ----------------------------------------
self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'portal'})
+ self.project_project.invalidate_cache(cr, uid)
# Do: Alfred reads project -> ok (employee ok public)
- self.project_project.read(cr, self.user_projectuser_id, pigs_id, ['name'])
+ self.project_project.read(cr, self.user_projectuser_id, [pigs_id], ['state'])
# Test: all project tasks visible
task_ids = self.project_task.search(cr, self.user_projectuser_id, [('project_id', '=', pigs_id)])
self.assertEqual(set(task_ids), test_task_ids,
'access rights: project user cannot see all tasks of a portal project')
# Do: Bert reads project -> crash, no group
- self.assertRaises(AccessError, self.project_project.read, cr, self.user_none_id, pigs_id, ['name'])
+ self.assertRaises(AccessError, self.project_project.read, cr, self.user_none_id, [pigs_id], ['state'])
# Test: no project task searchable
self.assertRaises(AccessError, self.project_task.search, cr, self.user_none_id, [('project_id', '=', pigs_id)])
@@ -151,7 +152,7 @@ class TestPortalProject(TestPortalProjectBase):
self.project_task.message_subscribe_users(cr, self.user_projectuser_id, [self.task_1_id, self.task_3_id], [self.user_portal_id])
# Do: Chell reads project -> ok (portal ok public)
- self.project_project.read(cr, self.user_portal_id, pigs_id, ['name'])
+ self.project_project.read(cr, self.user_portal_id, [pigs_id], ['state'])
# Test: only followed project tasks visible + assigned
task_ids = self.project_task.search(cr, self.user_portal_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_1_id, self.task_3_id, self.task_5_id])
@@ -159,7 +160,7 @@ class TestPortalProject(TestPortalProjectBase):
'access rights: portal user should see the followed tasks of a portal project')
# Do: Donovan reads project -> ko (public ko portal)
- self.assertRaises(except_orm, self.project_project.read, cr, self.user_public_id, pigs_id, ['name'])
+ self.assertRaises(except_orm, self.project_project.read, cr, self.user_public_id, [pigs_id], ['state'])
# Test: no project task visible
task_ids = self.project_task.search(cr, self.user_public_id, [('project_id', '=', pigs_id)])
self.assertFalse(task_ids, 'access rights: public user should not see tasks of a portal project')
@@ -171,9 +172,10 @@ class TestPortalProject(TestPortalProjectBase):
# CASE3: employee project
# ----------------------------------------
self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'employees'})
+ self.project_project.invalidate_cache(cr, uid)
# Do: Alfred reads project -> ok (employee ok employee)
- self.project_project.read(cr, self.user_projectuser_id, pigs_id, ['name'])
+ self.project_project.read(cr, self.user_projectuser_id, [pigs_id], ['state'])
# Test: all project tasks visible
task_ids = self.project_task.search(cr, self.user_projectuser_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_1_id, self.task_2_id, self.task_3_id, self.task_4_id, self.task_5_id, self.task_6_id])
@@ -181,16 +183,16 @@ class TestPortalProject(TestPortalProjectBase):
'access rights: project user cannot see all tasks of an employees project')
# Do: Bert reads project -> crash, no group
- self.assertRaises(AccessError, self.project_project.read, cr, self.user_none_id, pigs_id, ['name'])
+ self.assertRaises(AccessError, self.project_project.read, cr, self.user_none_id, [pigs_id], ['state'])
# Do: Chell reads project -> ko (portal ko employee)
- self.assertRaises(except_orm, self.project_project.read, cr, self.user_portal_id, pigs_id, ['name'])
+ self.assertRaises(except_orm, self.project_project.read, cr, self.user_portal_id, [pigs_id], ['state'])
# Test: no project task visible + assigned
task_ids = self.project_task.search(cr, self.user_portal_id, [('project_id', '=', pigs_id)])
self.assertFalse(task_ids, 'access rights: portal user should not see tasks of an employees project, even if assigned')
# Do: Donovan reads project -> ko (public ko employee)
- self.assertRaises(except_orm, self.project_project.read, cr, self.user_public_id, pigs_id, ['name'])
+ self.assertRaises(except_orm, self.project_project.read, cr, self.user_public_id, [pigs_id], ['state'])
# Test: no project task visible
task_ids = self.project_task.search(cr, self.user_public_id, [('project_id', '=', pigs_id)])
self.assertFalse(task_ids, 'access rights: public user should not see tasks of an employees project')
@@ -199,9 +201,10 @@ class TestPortalProject(TestPortalProjectBase):
# CASE4: followers project
# ----------------------------------------
self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'followers'})
+ self.project_project.invalidate_cache(cr, uid)
# Do: Alfred reads project -> ko (employee ko followers)
- self.assertRaises(except_orm, self.project_project.read, cr, self.user_projectuser_id, pigs_id, ['name'])
+ self.assertRaises(except_orm, self.project_project.read, cr, self.user_projectuser_id, [pigs_id], ['state'])
# Test: no project task visible
task_ids = self.project_task.search(cr, self.user_projectuser_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_4_id])
@@ -209,10 +212,10 @@ class TestPortalProject(TestPortalProjectBase):
'access rights: employee user should not see tasks of a not-followed followers project, only assigned')
# Do: Bert reads project -> crash, no group
- self.assertRaises(AccessError, self.project_project.read, cr, self.user_none_id, pigs_id, ['name'])
+ self.assertRaises(AccessError, self.project_project.read, cr, self.user_none_id, [pigs_id], ['state'])
# Do: Chell reads project -> ko (portal ko employee)
- self.assertRaises(except_orm, self.project_project.read, cr, self.user_portal_id, pigs_id, ['name'])
+ self.assertRaises(except_orm, self.project_project.read, cr, self.user_portal_id, [pigs_id], ['state'])
# Test: no project task visible
task_ids = self.project_task.search(cr, self.user_portal_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_5_id])
@@ -220,7 +223,7 @@ class TestPortalProject(TestPortalProjectBase):
'access rights: portal user should not see tasks of a not-followed followers project, only assigned')
# Do: Donovan reads project -> ko (public ko employee)
- self.assertRaises(except_orm, self.project_project.read, cr, self.user_public_id, pigs_id, ['name'])
+ self.assertRaises(except_orm, self.project_project.read, cr, self.user_public_id, [pigs_id], ['state'])
# Test: no project task visible
task_ids = self.project_task.search(cr, self.user_public_id, [('project_id', '=', pigs_id)])
self.assertFalse(task_ids, 'access rights: public user should not see tasks of a followers project')
@@ -230,7 +233,7 @@ class TestPortalProject(TestPortalProjectBase):
self.project_task.message_subscribe_users(cr, self.user_manager_id, [self.task_1_id, self.task_3_id], [self.user_portal_id, self.user_projectuser_id])
# Do: Alfred reads project -> ok (follower ok followers)
- self.project_project.read(cr, self.user_projectuser_id, pigs_id, ['name'])
+ self.project_project.read(cr, self.user_projectuser_id, [pigs_id], ['state'])
# Test: followed + assigned tasks visible
task_ids = self.project_task.search(cr, self.user_projectuser_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_1_id, self.task_3_id, self.task_4_id])
@@ -238,7 +241,7 @@ class TestPortalProject(TestPortalProjectBase):
'access rights: employee user should not see followed + assigned tasks of a follower project')
# Do: Chell reads project -> ok (follower ok follower)
- self.project_project.read(cr, self.user_portal_id, pigs_id, ['name'])
+ self.project_project.read(cr, self.user_portal_id, [pigs_id], ['state'])
# Test: followed + assigned tasks visible
task_ids = self.project_task.search(cr, self.user_portal_id, [('project_id', '=', pigs_id)])
test_task_ids = set([self.task_1_id, self.task_3_id, self.task_5_id])
@@ -246,4 +249,4 @@ class TestPortalProject(TestPortalProjectBase):
'access rights: employee user should not see followed + assigned tasks of a follower project')
# Do: Donovan reads project -> ko (public ko follower even if follower)
- self.assertRaises(except_orm, self.project_project.read, cr, self.user_public_id, pigs_id, ['name'])
+ self.assertRaises(except_orm, self.project_project.read, cr, self.user_public_id, [pigs_id], ['state'])
diff --git a/addons/portal_project_issue/tests/test_access_rights.py b/addons/portal_project_issue/tests/test_access_rights.py
index 7133c029d1a..6ca2883970d 100644
--- a/addons/portal_project_issue/tests/test_access_rights.py
+++ b/addons/portal_project_issue/tests/test_access_rights.py
@@ -50,7 +50,7 @@ class TestPortalProjectBase(TestPortalProjectBase):
class TestPortalIssue(TestPortalProjectBase):
- @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
+ @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
def test_00_project_access_rights(self):
""" Test basic project access rights, for project and portal_project """
cr, uid, pigs_id = self.cr, self.uid, self.project_pigs_id
diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py
index 1fc25171e7f..df8534e703d 100644
--- a/addons/procurement/procurement.py
+++ b/addons/procurement/procurement.py
@@ -134,7 +134,7 @@ class procurement_order(osv.osv):
('exception', 'Exception'),
('running', 'Running'),
('done', 'Done')
- ], 'Status', required=True, track_visibility='onchange'),
+ ], 'Status', required=True, track_visibility='onchange', copy=False),
}
_defaults = {
diff --git a/addons/procurement/wizard/schedulers_all.py b/addons/procurement/wizard/schedulers_all.py
index 34d4758c5d5..47f4dac440b 100644
--- a/addons/procurement/wizard/schedulers_all.py
+++ b/addons/procurement/wizard/schedulers_all.py
@@ -22,6 +22,7 @@
import threading
from openerp.osv import osv
+from openerp.api import Environment
class procurement_compute_all(osv.osv_memory):
_name = 'procurement.order.compute.all'
@@ -35,17 +36,18 @@ class procurement_compute_all(osv.osv_memory):
@param ids: List of IDs selected
@param context: A standard dictionary
"""
- proc_obj = self.pool.get('procurement.order')
- #As this function is in a new thread, i need to open a new cursor, because the old one may be closed
-
- new_cr = self.pool.cursor()
- user = self.pool.get('res.users').browse(new_cr, uid, uid, context=context)
- comps = [x.id for x in user.company_ids]
- for comp in comps:
- proc_obj.run_scheduler(new_cr, uid, use_new_cursor=new_cr.dbname, company_id = comp, context=context)
- #close the new cursor
- new_cr.close()
- return {}
+ with Environment.manage():
+ proc_obj = self.pool.get('procurement.order')
+ #As this function is in a new thread, i need to open a new cursor, because the old one may be closed
+
+ new_cr = self.pool.cursor()
+ user = self.pool.get('res.users').browse(new_cr, uid, uid, context=context)
+ comps = [x.id for x in user.company_ids]
+ for comp in comps:
+ proc_obj.run_scheduler(new_cr, uid, use_new_cursor=new_cr.dbname, company_id = comp, context=context)
+ #close the new cursor
+ new_cr.close()
+ return {}
def procure_calculation(self, cr, uid, ids, context=None):
"""
diff --git a/addons/procurement_jit/mrp_jit.xml b/addons/procurement_jit/mrp_jit.xml
deleted file mode 100644
index c00393ac04d..00000000000
--- a/addons/procurement_jit/mrp_jit.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
- check_conditions_confirm2wait()
-
-
-
-
diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py
index 069c331c126..3e00bd417ef 100644
--- a/addons/product/pricelist.py
+++ b/addons/product/pricelist.py
@@ -101,7 +101,7 @@ class product_pricelist(osv.osv):
'name': fields.char('Pricelist Name', required=True, translate=True),
'active': fields.boolean('Active', help="If unchecked, it will allow you to hide the pricelist without removing it."),
'type': fields.selection(_pricelist_type_get, 'Pricelist Type', required=True),
- 'version_id': fields.one2many('product.pricelist.version', 'pricelist_id', 'Pricelist Versions'),
+ 'version_id': fields.one2many('product.pricelist.version', 'pricelist_id', 'Pricelist Versions', copy=True),
'currency_id': fields.many2one('res.currency', 'Currency', required=True),
'company_id': fields.many2one('res.company', 'Company'),
}
@@ -167,19 +167,19 @@ class product_pricelist(osv.osv):
"currency_id": _get_currency
}
- def price_get_multi(self, cr, uid, pricelist_ids, products_by_qty_by_partner, context=None):
+ def price_get_multi(self, cr, uid, ids, products_by_qty_by_partner, context=None):
"""multi products 'price_get'.
- @param pricelist_ids:
+ @param ids:
@param products_by_qty:
@param partner:
@param context: {
'date': Date of the pricelist (%Y-%m-%d),}
@return: a dict of dict with product_id as key and a dict 'price by pricelist' as value
"""
- if not pricelist_ids:
- pricelist_ids = self.pool.get('product.pricelist').search(cr, uid, [], context=context)
+ if not ids:
+ ids = self.pool.get('product.pricelist').search(cr, uid, [], context=context)
results = {}
- for pricelist in self.browse(cr, uid, pricelist_ids, context=context):
+ for pricelist in self.browse(cr, uid, ids, context=context):
subres = self._price_get_multi(cr, uid, pricelist, products_by_qty_by_partner, context=context)
for product_id,price in subres.items():
results.setdefault(product_id, {})
@@ -247,14 +247,14 @@ class product_pricelist(osv.osv):
if rule.min_quantity and qtyrule.product_tmpl_id.id:
+ if rule.product_tmpl_id and product.id != rule.product_tmpl_id.id:
continue
if rule.product_id:
continue
else:
- if rule.product_tmpl_id and product.product_tmpl_id.id<>rule.product_tmpl_id.id:
+ if rule.product_tmpl_id and product.product_tmpl_id.id != rule.product_tmpl_id.id:
continue
- if rule.product_id and product.id<>rule.product_id.id:
+ if rule.product_id and product.id != rule.product_id.id:
continue
if rule.categ_id:
@@ -279,7 +279,7 @@ class product_pricelist(osv.osv):
context=context)
elif rule.base == -2:
for seller in product.seller_ids:
- if (not partner) or (seller.name.id<>partner):
+ if (not partner) or (seller.name.id != partner):
continue
qty_in_seller_uom = qty
from_uom = context.get('uom') or product.uom_id.id
@@ -325,7 +325,7 @@ class product_pricelist(osv.osv):
def price_get(self, cr, uid, ids, prod_id, qty, partner=None, context=None):
product = self.pool.get('product.product').browse(cr, uid, prod_id, context=context)
- res_multi = self.price_get_multi(cr, uid, pricelist_ids=ids, products_by_qty_by_partner=[(product, qty, partner)], context=context)
+ res_multi = self.price_get_multi(cr, uid, ids, products_by_qty_by_partner=[(product, qty, partner)], context=context)
res = res_multi[prod_id]
return res
@@ -340,9 +340,9 @@ class product_pricelist_version(osv.osv):
'active': fields.boolean('Active',
help="When a version is duplicated it is set to non active, so that the " \
"dates do not overlaps with original version. You should change the dates " \
- "and reactivate the pricelist"),
+ "and reactivate the pricelist", copy=False),
'items_id': fields.one2many('product.pricelist.item',
- 'price_version_id', 'Price List Items', required=True),
+ 'price_version_id', 'Price List Items', required=True, copy=True),
'date_start': fields.date('Start Date', help="First valid date for the version."),
'date_end': fields.date('End Date', help="Last valid date for the version."),
'company_id': fields.related('pricelist_id','company_id',type='many2one',
@@ -352,12 +352,6 @@ class product_pricelist_version(osv.osv):
'active': lambda *a: 1,
}
- # We desactivate duplicated pricelists, so that dates do not overlap
- def copy(self, cr, uid, id, default=None, context=None):
- if not default: default= {}
- default['active'] = False
- return super(product_pricelist_version, self).copy(cr, uid, id, default, context)
-
def _check_date(self, cursor, user, ids, context=None):
for pricelist_version in self.browse(cursor, user, ids, context=context):
if not pricelist_version.active:
diff --git a/addons/product/product.py b/addons/product/product.py
index d90165ae022..3d4d2350d1a 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -327,7 +327,7 @@ class product_attribute(osv.osv):
_description = "Product Attribute"
_columns = {
'name': fields.char('Name', translate=True, required=True),
- 'value_ids': fields.one2many('product.attribute.value', 'attribute_id', 'Values'),
+ 'value_ids': fields.one2many('product.attribute.value', 'attribute_id', 'Values', copy=True),
}
class product_attribute_value(osv.osv):
@@ -414,9 +414,9 @@ class product_template(osv.osv):
return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
def _is_product_variant(self, cr, uid, ids, name, arg, context=None):
- return self.is_product_variant(cr, uid, ids, name, arg, context=context)
+ return self._is_product_variant_impl(cr, uid, ids, name, arg, context=context)
- def is_product_variant(self, cr, uid, ids, name, arg, context=None):
+ def _is_product_variant_impl(self, cr, uid, ids, name, arg, context=None):
prod = self.pool.get('product.product')
res = dict.fromkeys(ids, False)
ctx = dict(context, active_test=True)
@@ -848,7 +848,7 @@ class product_product(osv.osv):
res[p.id] = (data['code'] and ('['+data['code']+'] ') or '') + (data['name'] or '')
return res
- def is_product_variant(self, cr, uid, ids, name, arg, context=None):
+ def _is_product_variant_impl(self, cr, uid, ids, name, arg, context=None):
return dict.fromkeys(ids, True)
def _get_name_template_ids(self, cr, uid, ids, context=None):
@@ -1157,7 +1157,7 @@ class product_supplierinfo(osv.osv):
'qty': fields.function(_calc_qty, store=True, type='float', string='Quantity', multi="qty", help="This is a quantity which is converted into Default Unit of Measure."),
'product_tmpl_id' : fields.many2one('product.template', 'Product Template', required=True, ondelete='cascade', select=True, oldname='product_id'),
'delay' : fields.integer('Delivery Lead Time', required=True, help="Lead time in days between the confirmation of the purchase order and the reception of the products in your warehouse. Used by the scheduler for automatic computation of the purchase order planning."),
- 'pricelist_ids': fields.one2many('pricelist.partnerinfo', 'suppinfo_id', 'Supplier Pricelist'),
+ 'pricelist_ids': fields.one2many('pricelist.partnerinfo', 'suppinfo_id', 'Supplier Pricelist', copy=True),
'company_id':fields.many2one('res.company','Company',select=1),
}
_defaults = {
diff --git a/addons/product_expiry/product_expiry.py b/addons/product_expiry/product_expiry.py
index 6979344bbcc..392e574a0bb 100644
--- a/addons/product_expiry/product_expiry.py
+++ b/addons/product_expiry/product_expiry.py
@@ -62,8 +62,7 @@ class stock_production_lot(osv.osv):
for f in ('life_date', 'use_date', 'removal_date', 'alert_date'):
if not getattr(obj, f):
towrite.append(f)
- if context is None:
- context = {}
+ context = dict(context or {})
context['product_id'] = obj.product_id.id
self.write(cr, uid, [obj.id], self.default_get(cr, uid, towrite, context=context))
return newid
diff --git a/addons/product_margin/wizard/product_margin.py b/addons/product_margin/wizard/product_margin.py
index 7a56ed2e13d..c318d66c0ef 100644
--- a/addons/product_margin/wizard/product_margin.py
+++ b/addons/product_margin/wizard/product_margin.py
@@ -24,23 +24,26 @@ import time
from openerp.osv import fields, osv
from openerp.tools.translate import _
+
class product_margin(osv.osv_memory):
_name = 'product.margin'
_description = 'Product Margin'
_columns = {
'from_date': fields.date('From'),
'to_date': fields.date('To'),
- 'invoice_state':fields.selection([
- ('paid','Paid'),
- ('open_paid','Open and Paid'),
- ('draft_open_paid','Draft, Open and Paid'),
- ],'Invoice State', select=True, required=True),
+ 'invoice_state': fields.selection([
+ ('paid', 'Paid'),
+ ('open_paid', 'Open and Paid'),
+ ('draft_open_paid', 'Draft, Open and Paid'),
+ ], 'Invoice State', select=True, required=True),
}
+
_defaults = {
'from_date': time.strftime('%Y-01-01'),
'to_date': time.strftime('%Y-12-31'),
'invoice_state': "open_paid",
}
+
def action_open_window(self, cr, uid, ids, context=None):
"""
@param cr: the current row, from the database cursor,
@@ -49,36 +52,44 @@ class product_margin(osv.osv_memory):
@return:
"""
- if context is None:
- context = {}
- mod_obj = self.pool.get('ir.model.data')
- result = mod_obj._get_id(cr, uid, 'product', 'product_search_form_view')
- id = mod_obj.read(cr, uid, result, ['res_id'], context=context)
- cr.execute('select id,name from ir_ui_view where name=%s and type=%s', ('product.margin.graph', 'graph'))
- view_res3 = cr.fetchone()[0]
- cr.execute('select id,name from ir_ui_view where name=%s and type=%s', ('product.margin.form.inherit', 'form'))
- view_res2 = cr.fetchone()[0]
- cr.execute('select id,name from ir_ui_view where name=%s and type=%s', ('product.margin.tree', 'tree'))
- view_res = cr.fetchone()[0]
+ context = dict(context or {})
+
+ def ref(module, xml_id):
+ proxy = self.pool.get('ir.model.data')
+ return proxy.get_object_reference(cr, uid, module, xml_id)
+
+ model, search_view_id = ref('product', 'product_search_form_view')
+ model, graph_view_id = ref('product_margin', 'view_product_margin_graph')
+ model, form_view_id = ref('product_margin', 'view_product_margin_form')
+ model, tree_view_id = ref('product_margin', 'view_product_margin_tree')
#get the current product.margin object to obtain the values from it
- product_margin_obj = self.browse(cr, uid, ids, context=context)[0]
+ records = self.browse(cr, uid, ids, context=context)
+ record = records[0]
- context.update(invoice_state = product_margin_obj.invoice_state)
- if product_margin_obj.from_date:
- context.update(date_from = product_margin_obj.from_date)
- if product_margin_obj.to_date:
- context.update(date_to = product_margin_obj.to_date)
+ context.update(invoice_state=record.invoice_state)
+
+ if record.from_date:
+ context.update(date_from=record.from_date)
+
+ if record.to_date:
+ context.update(date_to=record.to_date)
+
+ views = [
+ (tree_view_id, 'tree'),
+ (form_view_id, 'form'),
+ (graph_view_id, 'graph')
+ ]
return {
'name': _('Product Margins'),
'context': context,
'view_type': 'form',
"view_mode": 'tree,form,graph',
- 'res_model':'product.product',
+ 'res_model': 'product.product',
'type': 'ir.actions.act_window',
- 'views': [(view_res,'tree'), (view_res2,'form'), (view_res3,'graph')],
+ 'views': views,
'view_id': False,
- 'search_view_id': id['res_id']
+ 'search_view_id': search_view_id,
}
diff --git a/addons/product_visible_discount/product_visible_discount.py b/addons/product_visible_discount/product_visible_discount.py
index 605a71e8b80..1c650e247da 100644
--- a/addons/product_visible_discount/product_visible_discount.py
+++ b/addons/product_visible_discount/product_visible_discount.py
@@ -49,7 +49,7 @@ class sale_order_line(osv.osv):
field_name = 'list_price'
product = product_obj.browse(cr, uid, product_id, context)
- product_read = product_obj.read(cr, uid, product_id, [field_name], context=context)
+ product_read = product_obj.read(cr, uid, [product_id], [field_name], context=context)[0]
factor = 1.0
if uom and uom != product.uom_id.id:
diff --git a/addons/project/project.py b/addons/project/project.py
index f95f3e4517c..78c3904ec07 100644
--- a/addons/project/project.py
+++ b/addons/project/project.py
@@ -273,7 +273,13 @@ class project(osv.osv):
"- Employees Only: employees see all tasks or issues\n"
"- Followers Only: employees see only the followed tasks or issues; if portal\n"
" is activated, portal users see the followed tasks or issues."),
- 'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'), ('cancelled', 'Cancelled'),('pending','Pending'),('close','Closed')], 'Status', required=True,),
+ 'state': fields.selection([('template', 'Template'),
+ ('draft','New'),
+ ('open','In Progress'),
+ ('cancelled', 'Cancelled'),
+ ('pending','Pending'),
+ ('close','Closed')],
+ 'Status', required=True, copy=False),
'doc_count': fields.function(
_get_attached_docs, string="Number of documents attached", type='integer'
)
@@ -332,36 +338,28 @@ class project(osv.osv):
task_obj = self.pool.get('project.task')
proj = self.browse(cr, uid, old_project_id, context=context)
for task in proj.tasks:
- map_task_id[task.id] = task_obj.copy(cr, uid, task.id, {}, context=context)
+ # preserve task name and stage, normally altered during copy
+ defaults = {'stage_id': task.stage_id.id,
+ 'name': task.name}
+ map_task_id[task.id] = task_obj.copy(cr, uid, task.id, defaults, context=context)
self.write(cr, uid, [new_project_id], {'tasks':[(6,0, map_task_id.values())]})
task_obj.duplicate_task(cr, uid, map_task_id, context=context)
return True
def copy(self, cr, uid, id, default=None, context=None):
- if context is None:
- context = {}
if default is None:
default = {}
-
+ context = dict(context or {})
context['active_test'] = False
- default['state'] = 'open'
- default['line_ids'] = []
- default['tasks'] = []
-
- # Don't prepare (expensive) data to copy children (analytic accounts),
- # they are discarded in analytic.copy(), and handled in duplicate_template()
- default['child_ids'] = []
-
proj = self.browse(cr, uid, id, context=context)
- if not default.get('name', False):
+ if not default.get('name'):
default.update(name=_("%s (copy)") % (proj.name))
res = super(project, self).copy(cr, uid, id, default, context)
self.map_tasks(cr, uid, id, res, context=context)
return res
def duplicate_template(self, cr, uid, ids, context=None):
- if context is None:
- context = {}
+ context = dict(context or {})
data_obj = self.pool.get('ir.model.data')
result = []
for proj in self.browse(cr, uid, ids, context=context):
@@ -696,29 +694,11 @@ class task(osv.osv):
def copy_data(self, cr, uid, id, default=None, context=None):
if default is None:
default = {}
- default = default or {}
- default.update({'work_ids':[], 'date_start': False, 'date_end': False, 'date_deadline': False})
- if not default.get('remaining_hours', False):
- default['remaining_hours'] = float(self.read(cr, uid, id, ['planned_hours'])['planned_hours'])
- default['active'] = True
- if not default.get('name', False):
- default['name'] = self.browse(cr, uid, id, context=context).name or ''
- if not context.get('copy',False):
- new_name = _("%s (copy)") % (default.get('name', ''))
- default.update({'name':new_name})
+ if not default.get('name'):
+ current = self.browse(cr, uid, id, context=context)
+ default['name'] = _("%s (copy)") % current.name
return super(task, self).copy_data(cr, uid, id, default, context)
- def copy(self, cr, uid, id, default=None, context=None):
- if context is None:
- context = {}
- if default is None:
- default = {}
- if not context.get('copy', False):
- stage = self._get_default_stage_id(cr, uid, context=context)
- if stage:
- default['stage_id'] = stage
- return super(task, self).copy(cr, uid, id, default, context)
-
def _is_template(self, cr, uid, ids, field_name, arg, context=None):
res = {}
for task in self.browse(cr, uid, ids, context=context):
@@ -741,7 +721,7 @@ class task(osv.osv):
'priority': fields.selection([('0','Low'), ('1','Normal'), ('2','High')], 'Priority', select=True),
'sequence': fields.integer('Sequence', select=True, help="Gives the sequence order when displaying a list of tasks."),
'stage_id': fields.many2one('project.task.type', 'Stage', track_visibility='onchange', select=True,
- domain="[('project_ids', '=', project_id)]"),
+ domain="[('project_ids', '=', project_id)]", copy=False),
'categ_ids': fields.many2many('project.category', string='Tags'),
'kanban_state': fields.selection([('normal', 'In Progress'),('blocked', 'Blocked'),('done', 'Ready for next stage')], 'Kanban State',
track_visibility='onchange',
@@ -749,13 +729,13 @@ class task(osv.osv):
" * Normal is the default situation\n"
" * Blocked indicates something is preventing the progress of this task\n"
" * Ready for next stage indicates the task is ready to be pulled to the next stage",
- required=False),
+ required=False, copy=False),
'create_date': fields.datetime('Create Date', readonly=True, select=True),
'write_date': fields.datetime('Last Modification Date', readonly=True, select=True), #not displayed in the view but it might be useful with base_action_rule module (and it needs to be defined first for that)
- 'date_start': fields.datetime('Starting Date',select=True),
- 'date_end': fields.datetime('Ending Date',select=True),
- 'date_deadline': fields.date('Deadline',select=True),
- 'date_last_stage_update': fields.datetime('Last Stage Update', select=True),
+ 'date_start': fields.datetime('Starting Date', select=True, copy=False),
+ 'date_end': fields.datetime('Ending Date', select=True, copy=False),
+ 'date_deadline': fields.date('Deadline', select=True, copy=False),
+ 'date_last_stage_update': fields.datetime('Last Stage Update', select=True, copy=False),
'project_id': fields.many2one('project.project', 'Project', ondelete='set null', select=True, track_visibility='onchange', change_default=True),
'parent_ids': fields.many2many('project.task', 'project_task_parent_rel', 'task_id', 'parent_id', 'Parent Tasks'),
'child_ids': fields.many2many('project.task', 'project_task_parent_rel', 'parent_id', 'task_id', 'Delegated Tasks'),
@@ -888,6 +868,7 @@ class task(osv.osv):
return res
def get_empty_list_help(self, cr, uid, help, context=None):
+ context = dict(context or {})
context['empty_list_help_id'] = context.get('default_project_id')
context['empty_list_help_model'] = 'project.project'
context['empty_list_help_document_name'] = _("tasks")
@@ -1011,8 +992,7 @@ class task(osv.osv):
# ------------------------------------------------
def create(self, cr, uid, vals, context=None):
- if context is None:
- context = {}
+ context = dict(context or {})
# for default stage
if vals.get('project_id') and not context.get('default_project_id'):
@@ -1044,7 +1024,7 @@ class task(osv.osv):
new_stage = vals.get('stage_id')
vals_reset_kstate = dict(vals, kanban_state='normal')
for t in self.browse(cr, uid, ids, context=context):
- write_vals = vals_reset_kstate if t.stage_id != new_stage else vals
+ write_vals = vals_reset_kstate if t.stage_id.id != new_stage else vals
super(task, self).write(cr, uid, [t.id], write_vals, context=context)
result = True
else:
@@ -1153,24 +1133,29 @@ class project_work(osv.osv):
}
_order = "date desc"
- def create(self, cr, uid, vals, *args, **kwargs):
+ def create(self, cr, uid, vals, context=None):
if 'hours' in vals and (not vals['hours']):
vals['hours'] = 0.00
if 'task_id' in vals:
cr.execute('update project_task set remaining_hours=remaining_hours - %s where id=%s', (vals.get('hours',0.0), vals['task_id']))
- return super(project_work,self).create(cr, uid, vals, *args, **kwargs)
+ self.pool.get('project.task').invalidate_cache(cr, uid, ['remaining_hours'], [vals['task_id']], context=context)
+ return super(project_work,self).create(cr, uid, vals, context=context)
def write(self, cr, uid, ids, vals, context=None):
if 'hours' in vals and (not vals['hours']):
vals['hours'] = 0.00
if 'hours' in vals:
+ task_obj = self.pool.get('project.task')
for work in self.browse(cr, uid, ids, context=context):
cr.execute('update project_task set remaining_hours=remaining_hours - %s + (%s) where id=%s', (vals.get('hours',0.0), work.hours, work.task_id.id))
+ task_obj.invalidate_cache(cr, uid, ['remaining_hours'], [work.task_id.id], context=context)
return super(project_work,self).write(cr, uid, ids, vals, context)
def unlink(self, cr, uid, ids, *args, **kwargs):
+ task_obj = self.pool.get('project.task')
for work in self.browse(cr, uid, ids):
cr.execute('update project_task set remaining_hours=remaining_hours + %s where id=%s', (work.hours, work.task_id.id))
+ task_obj.invalidate_cache(cr, uid, ['remaining_hours'], [work.task_id.id], context=context)
return super(project_work,self).unlink(cr, uid, ids,*args, **kwargs)
diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml
index c151464fb5c..41e100cec1f 100644
--- a/addons/project/project_view.xml
+++ b/addons/project/project_view.xml
@@ -8,8 +8,16 @@
groups="group_project_manager,group_project_user"
sequence="40"/>
-
-
+
+
+
project.task.search.form
diff --git a/addons/project/tests/test_project_flow.py b/addons/project/tests/test_project_flow.py
index ecbe66a0cea..58263f6ea8a 100644
--- a/addons/project/tests/test_project_flow.py
+++ b/addons/project/tests/test_project_flow.py
@@ -51,7 +51,7 @@ Integrator at Agrolait"""
class TestProjectFlow(TestProjectBase):
- @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
+ @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
def test_00_project_process(self):
""" Testing project management """
cr, uid, user_projectuser_id, user_projectmanager_id, project_pigs_id = self.cr, self.uid, self.user_projectuser_id, self.user_projectmanager_id, self.project_pigs_id
@@ -131,8 +131,8 @@ class TestProjectFlow(TestProjectBase):
# Test: one task created by mailgateway administrator
self.assertEqual(len(frogs), 1, 'project: message_process: a new project.task should have been created')
task = self.project_task.browse(cr, user_projectuser_id, frogs[0])
- res = self.project_task.perm_read(cr, uid, [task.id], details=False)
- self.assertEqual(res[0].get('create_uid'), uid,
+ res = self.project_task.get_metadata(cr, uid, [task.id])[0].get('create_uid') or [None]
+ self.assertEqual(res[0], uid,
'project: message_process: task should have been created by uid as alias_user_id is False on the alias')
# Test: messages
self.assertEqual(len(task.message_ids), 3,
diff --git a/addons/project/wizard/project_task_delegate.py b/addons/project/wizard/project_task_delegate.py
index a0e137eda0d..e3c935d4f5d 100644
--- a/addons/project/wizard/project_task_delegate.py
+++ b/addons/project/wizard/project_task_delegate.py
@@ -125,7 +125,7 @@ class project_task_delegate(osv.osv_memory):
action_model, action_id = models_data.get_object_reference(cr, uid, 'project', 'action_view_task')
view_model, task_view_form_id = models_data.get_object_reference(cr, uid, 'project', 'view_task_form2')
view_model, task_view_tree_id = models_data.get_object_reference(cr, uid, 'project', 'view_task_tree2')
- action = self.pool[action_model].read(cr, uid, action_id, context=context)
+ action = self.pool[action_model].read(cr, uid, [action_id], context=context)[0]
action['res_id'] = delegated_tasks[task_id]
action['view_id'] = False
action['views'] = [(task_view_form_id, 'form'), (task_view_tree_id, 'tree')]
diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py
index 8b7c70e41e1..0c3ba5683fd 100644
--- a/addons/project_issue/project_issue.py
+++ b/addons/project_issue/project_issue.py
@@ -264,7 +264,7 @@ class project_issue(osv.Model):
'version_id': fields.many2one('project.issue.version', 'Version'),
'stage_id': fields.many2one ('project.task.type', 'Stage',
track_visibility='onchange', select=True,
- domain="[('project_ids', '=', project_id)]"),
+ domain="[('project_ids', '=', project_id)]", copy=False),
'project_id': fields.many2one('project.project', 'Project', track_visibility='onchange', select=True),
'duration': fields.float('Duration'),
'task_id': fields.many2one('project.task', 'Task', domain="[('project_id','=',project_id)]"),
@@ -306,7 +306,7 @@ class project_issue(osv.Model):
}
def copy(self, cr, uid, id, default=None, context=None):
- issue = self.read(cr, uid, id, ['name'], context=context)
+ issue = self.read(cr, uid, [id], ['name'], context=context)[0]
if not default:
default = {}
default = default.copy()
@@ -315,8 +315,7 @@ class project_issue(osv.Model):
context=context)
def create(self, cr, uid, vals, context=None):
- if context is None:
- context = {}
+ context = dict(context or {})
if vals.get('project_id') and not context.get('default_project_id'):
context['default_project_id'] = vals.get('project_id')
@@ -353,6 +352,7 @@ class project_issue(osv.Model):
return {'value': result}
def get_empty_list_help(self, cr, uid, help, context=None):
+ context = dict(context or {})
context['empty_list_help_model'] = 'project.project'
context['empty_list_help_id'] = context.get('default_project_id')
context['empty_list_help_document_name'] = _("issues")
@@ -437,9 +437,7 @@ class project_issue(osv.Model):
"""
if custom_values is None:
custom_values = {}
- if context is None:
- context = {}
- context['state_to'] = 'draft'
+ context = dict(context or {}, state_to='draft')
defaults = {
'name': msg.get('subject') or _("No Subject"),
'email_from': msg.get('from'),
diff --git a/addons/project_timesheet/project_timesheet.py b/addons/project_timesheet/project_timesheet.py
index f6c2321d1f9..692249b3ff4 100644
--- a/addons/project_timesheet/project_timesheet.py
+++ b/addons/project_timesheet/project_timesheet.py
@@ -137,6 +137,7 @@ class project_work(osv.osv):
amount = vals_line['unit_amount']
prod_id = vals_line['product_id']
unit = False
+ context = dict(context, no_store_function=False)
timeline_id = timesheet_obj.create(cr, uid, vals_line, context=context)
# Compute based on pricetype
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py
index 18f31c3bd58..e8622de493d 100644
--- a/addons/purchase/purchase.py
+++ b/addons/purchase/purchase.py
@@ -28,7 +28,7 @@ from openerp.tools.safe_eval import safe_eval as eval
from openerp.osv import fields, osv
from openerp.tools.translate import _
import openerp.addons.decimal_precision as dp
-from openerp.osv.orm import browse_record, browse_null
+from openerp.osv.orm import browse_record_list, browse_record, browse_null
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, DATETIME_FORMATS_MAP
class purchase_order(osv.osv):
@@ -66,6 +66,7 @@ class purchase_order(osv.osv):
(date_planned=%s or date_planned<%s)""", (value,po.id,po.minimum_planned_date,value))
cr.execute("""update purchase_order set
minimum_planned_date=%s where id=%s""", (value, po.id))
+ self.invalidate_cache(cr, uid, context=context)
return True
def _minimum_planned_date(self, cr, uid, ids, field_name, arg, context=None):
@@ -194,14 +195,26 @@ class purchase_order(osv.osv):
},
}
_columns = {
- 'name': fields.char('Order Reference', required=True, select=True, help="Unique number of the purchase order, computed automatically when the purchase order is created."),
- 'origin': fields.char('Source Document',
- help="Reference of the document that generated this purchase order request; a sales order or an internal procurement request."
- ),
- 'partner_ref': fields.char('Supplier Reference', states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]},
- help="Reference of the sales order or bid sent by your supplier. It's mainly used to do the matching when you receive the products as this reference is usually written on the delivery order sent by your supplier."),
- 'date_order':fields.date('Order Date', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}, select=True, help="Date on which this document has been created."),
- 'date_approve':fields.date('Date Approved', readonly=1, select=True, help="Date on which purchase order has been approved"),
+ 'name': fields.char('Order Reference', required=True, select=True, copy=False,
+ help="Unique number of the purchase order, "
+ "computed automatically when the purchase order is created."),
+ 'origin': fields.char('Source Document', copy=False,
+ help="Reference of the document that generated this purchase order "
+ "request; a sales order or an internal procurement request."),
+ 'partner_ref': fields.char('Supplier Reference', states={'confirmed':[('readonly',True)],
+ 'approved':[('readonly',True)],
+ 'done':[('readonly',True)]},
+ copy=False,
+ help="Reference of the sales order or bid sent by your supplier. "
+ "It's mainly used to do the matching when you receive the "
+ "products as this reference is usually written on the "
+ "delivery order sent by your supplier."),
+ 'date_order':fields.date('Order Date', required=True, states={'confirmed':[('readonly',True)],
+ 'approved':[('readonly',True)]},
+ select=True, help="Date on which this document has been created.",
+ copy=False),
+ 'date_approve':fields.date('Date Approved', readonly=1, select=True, copy=False,
+ help="Date on which purchase order has been approved"),
'partner_id':fields.many2one('res.partner', 'Supplier', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]},
change_default=True, track_visibility='always'),
'dest_address_id':fields.many2one('res.partner', 'Customer Address (Direct Delivery)',
@@ -212,15 +225,31 @@ class purchase_order(osv.osv):
'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')], states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]} ),
'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, help="The pricelist sets the currency used for this purchase order. It also computes the supplier price for the selected products/quantities."),
'currency_id': fields.many2one('res.currency','Currency', readonly=True, required=True,states={'draft': [('readonly', False)],'sent': [('readonly', False)]}),
- 'state': fields.selection(STATE_SELECTION, 'Status', readonly=True, help="The status of the purchase order or the quotation request. A request for quotation is a purchase order in a 'Draft' status. Then the order has to be confirmed by the user, the status switch to 'Confirmed'. Then the supplier must confirm the order to change the status to 'Approved'. When the purchase order is paid and received, the status becomes 'Done'. If a cancel action occurs in the invoice or in the reception of goods, the status becomes in exception.", select=True),
- 'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order Lines', states={'approved':[('readonly',True)],'done':[('readonly',True)]}),
- 'validator' : fields.many2one('res.users', 'Validated by', readonly=True),
+ 'state': fields.selection(STATE_SELECTION, 'Status', readonly=True,
+ help="The status of the purchase order or the quotation request. "
+ "A request for quotation is a purchase order in a 'Draft' status. "
+ "Then the order has to be confirmed by the user, the status switch "
+ "to 'Confirmed'. Then the supplier must confirm the order to change "
+ "the status to 'Approved'. When the purchase order is paid and "
+ "received, the status becomes 'Done'. If a cancel action occurs in "
+ "the invoice or in the reception of goods, the status becomes "
+ "in exception.",
+ select=True, copy=False),
+ 'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order Lines',
+ states={'approved':[('readonly',True)],
+ 'done':[('readonly',True)]},
+ copy=True),
+ 'validator' : fields.many2one('res.users', 'Validated by', readonly=True, copy=False),
'notes': fields.text('Terms and Conditions'),
- 'invoice_ids': fields.many2many('account.invoice', 'purchase_invoice_rel', 'purchase_id', 'invoice_id', 'Invoices', help="Invoices generated for a purchase order"),
+ 'invoice_ids': fields.many2many('account.invoice', 'purchase_invoice_rel', 'purchase_id',
+ 'invoice_id', 'Invoices', copy=False,
+ help="Invoices generated for a purchase order"),
'picking_ids': fields.function(_get_picking_ids, method=True, type='one2many', relation='stock.picking', string='Picking List', help="This is the list of reception operations that have been generated for this purchase order."),
- 'shipped':fields.boolean('Received', readonly=True, select=True, help="It indicates that a picking has been done"),
+ 'shipped':fields.boolean('Received', readonly=True, select=True, copy=False,
+ help="It indicates that a picking has been done"),
'shipped_rate': fields.function(_shipped_rate, string='Received Ratio', type='float'),
- 'invoiced': fields.function(_invoiced, string='Invoice Received', type='boolean', help="It indicates that an invoice has been validated"),
+ 'invoiced': fields.function(_invoiced, string='Invoice Received', type='boolean', copy=False,
+ help="It indicates that an invoice has been validated"),
'invoiced_rate': fields.function(_invoiced_rate, string='Invoiced', type='float'),
'invoice_method': fields.selection([('manual','Based on Purchase Order lines'),('order','Based on generated draft invoice'),('picking','Based on incoming shipments')], 'Invoicing Control', required=True,
readonly=True, states={'draft':[('readonly',False)], 'sent':[('readonly',False)]},
@@ -284,9 +313,7 @@ class purchase_order(osv.osv):
def create(self, cr, uid, vals, context=None):
if vals.get('name','/')=='/':
vals['name'] = self.pool.get('ir.sequence').get(cr, uid, 'purchase.order') or '/'
- if context is None:
- context = {}
- context.update({'mail_create_nolog': True})
+ context = dict(context or {}, mail_create_nolog=True)
order = super(purchase_order, self).create(cr, uid, vals, context=context)
self.message_post(cr, uid, [order], body=_("RFQ created"), context=context)
return order
@@ -388,6 +415,7 @@ class purchase_order(osv.osv):
'''
This function returns an action that display existing invoices of given sales order ids. It can either be a in a list or in a form view, if there is only one invoice to show.
'''
+ context = dict(context or {})
mod_obj = self.pool.get('ir.model.data')
wizard_obj = self.pool.get('purchase.order.line_invoice')
#compute the number of invoices to display
@@ -597,8 +625,7 @@ class purchase_order(osv.osv):
:return: ID of created invoice.
:rtype: int
"""
- if context is None:
- context = {}
+ context = dict(context or {})
inv_obj = self.pool.get('account.invoice')
inv_line_obj = self.pool.get('account.invoice.line')
@@ -620,7 +647,7 @@ class purchase_order(osv.osv):
inv_line_data = self._prepare_inv_line(cr, uid, acc_id, po_line, context=context)
inv_line_id = inv_line_obj.create(cr, uid, inv_line_data, context=context)
inv_lines.append(inv_line_id)
- po_line.write({'invoice_lines': [(4, inv_line_id)]}, context=context)
+ po_line.write({'invoice_lines': [(4, inv_line_id)]})
# get invoice data and create invoice
inv_data = self._prepare_invoice(cr, uid, order, inv_lines, context=context)
@@ -630,7 +657,7 @@ class purchase_order(osv.osv):
inv_obj.button_compute(cr, uid, [inv_id], context=context, set_total=True)
# Link this new invoice to related purchase order
- order.write({'invoice_ids': [(4, inv_id)]}, context=context)
+ order.write({'invoice_ids': [(4, inv_id)]})
res = inv_id
return res
@@ -808,20 +835,6 @@ class purchase_order(osv.osv):
self.message_post(cr, uid, ids, body=_("Products received"), context=context)
return True
- def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({
- 'state':'draft',
- 'shipped':False,
- 'invoiced':False,
- 'invoice_ids': [],
- 'origin': '',
- 'partner_ref': '',
- 'name': self.pool.get('ir.sequence').get(cr, uid, 'purchase.order'),
- })
- return super(purchase_order, self).copy(cr, uid, id, default, context)
-
def do_merge(self, cr, uid, ids, context=None):
"""
To merge similar type of purchase orders.
@@ -853,14 +866,13 @@ class purchase_order(osv.osv):
field_val = field_val.id
elif isinstance(field_val, browse_null):
field_val = False
- elif isinstance(field_val, list):
+ elif isinstance(field_val, browse_record_list):
field_val = ((6, 0, tuple([v.id for v in field_val])),)
list_key.append((field, field_val))
list_key.sort()
return tuple(list_key)
- if context is None:
- context = {}
+ context = dict(context or {})
# Compute what the new orders should contain
new_orders = {}
@@ -958,13 +970,16 @@ class purchase_order_line(osv.osv):
'order_id': fields.many2one('purchase.order', 'Order Reference', select=True, required=True, ondelete='cascade'),
'account_analytic_id':fields.many2one('account.analytic.account', 'Analytic Account',),
'company_id': fields.related('order_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True),
- 'state': fields.selection([('draft', 'Draft'), ('confirmed', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled')], 'Status', required=True, readonly=True,
+ 'state': fields.selection([('draft', 'Draft'), ('confirmed', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled')],
+ 'Status', required=True, readonly=True, copy=False,
help=' * The \'Draft\' status is set automatically when purchase order in draft status. \
\n* The \'Confirmed\' status is set automatically as confirm when purchase order in confirm status. \
\n* The \'Done\' status is set automatically when purchase order is set as done. \
\n* The \'Cancelled\' status is set automatically when user cancel purchase order.'),
- 'invoice_lines': fields.many2many('account.invoice.line', 'purchase_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True),
- 'invoiced': fields.boolean('Invoiced', readonly=True),
+ 'invoice_lines': fields.many2many('account.invoice.line', 'purchase_order_line_invoice_rel',
+ 'order_line_id', 'invoice_id', 'Invoice Lines',
+ readonly=True, copy=False),
+ 'invoiced': fields.boolean('Invoiced', readonly=True, copy=False),
'partner_id': fields.related('order_id','partner_id',string='Partner',readonly=True,type="many2one", relation="res.partner", store=True),
'date_order': fields.related('order_id','date_order',string='Order Date',readonly=True,type="date"),
'procurement_ids': fields.one2many('procurement.order', 'purchase_line_id', string='Associated procurements'),
@@ -979,12 +994,6 @@ class purchase_order_line(osv.osv):
_name = 'purchase.order.line'
_description = 'Purchase Order Line'
- def copy_data(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({'state':'draft', 'move_ids':[], 'invoiced':0, 'invoice_lines':[], 'procurement_ids': False})
- return super(purchase_order_line, self).copy_data(cr, uid, id, default, context)
-
def unlink(self, cr, uid, ids, context=None):
for line in self.browse(cr, uid, ids, context=context):
if line.state not in ['draft', 'cancel']:
diff --git a/addons/purchase/purchase_data.yml b/addons/purchase/purchase_data.yml
index a122c932b24..92a88bc0c09 100644
--- a/addons/purchase/purchase_data.yml
+++ b/addons/purchase/purchase_data.yml
@@ -1,5 +1,5 @@
-
- !python {model: ir.values, id: purchase_default_set}: |
+ !python {model: ir.values}: |
whr = self.pool.get('stock.warehouse').browse(cr, uid, ref('stock.warehouse0'), context=context)
self.set_default(cr, uid, 'purchase.order', 'picking_type_id', whr.in_type_id.id, for_all_users=True, company_id=True, condition=False)
-
diff --git a/addons/purchase/stock.py b/addons/purchase/stock.py
index 26d2ff1002f..c719e7b51ec 100644
--- a/addons/purchase/stock.py
+++ b/addons/purchase/stock.py
@@ -50,9 +50,7 @@ class stock_move(osv.osv):
default = {}
if not default.get('split_from'):
#we don't want to propagate the link to the purchase order line except in case of move split
- default.update({
- 'purchase_line_id': False,
- })
+ default['purchase_line_id'] = False
return super(stock_move, self).copy(cr, uid, id, default, context)
diff --git a/addons/purchase/test/fifo_price.yml b/addons/purchase/test/fifo_price.yml
index 07bcd4f3070..d98e536c532 100644
--- a/addons/purchase/test/fifo_price.yml
+++ b/addons/purchase/test/fifo_price.yml
@@ -41,8 +41,8 @@
Process the reception of purchase order 1 and set date
-
!python {model: stock.picking}: |
- picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo1")).picking_ids[0]
- picking_obj.do_transfer(context=context)
+ picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo1"), context=context).picking_ids[0]
+ picking_obj.do_transfer()
-
Check the standard price of the product (fifo icecream), that should have not changed because the standard price is supposed to be updated only when goods are going out of the stock
-
@@ -69,8 +69,8 @@
Process the reception of purchase order 2
-
!python {model: stock.picking}: |
- picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo2")).picking_ids[0]
- picking_obj.do_transfer(context=context)
+ picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo2"), context=context).picking_ids[0]
+ picking_obj.do_transfer()
-
Check the standard price of the product, that should have not changed because the standard price is supposed to be updated only when goods are going out of the stock
-
@@ -101,8 +101,8 @@
Process the delivery of the outgoing shipment
-
!python {model: stock.picking}: |
- pick_obj = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_fifo_shipment"))
- pick_obj.do_transfer(context=context)
+ pick_obj = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_fifo_shipment"), context=context)
+ pick_obj.do_transfer()
-
Check product standard price changed to 65.0 (because last outgoing shipment was made of 10 kg at 50€ and 10 kg at 80€)
-
@@ -133,8 +133,8 @@
Process the delivery of the outgoing shipment
-
!python {model: stock.picking}: |
- pick_obj = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_fifo_shipment_uom"))
- pick_obj.do_transfer(context=context)
+ pick_obj = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_fifo_shipment_uom"), context=context)
+ pick_obj.do_transfer()
-
Check product price changed to 80.0 (because last outgoing shipment was made of 0.5 kg at 80€)
-
@@ -190,8 +190,8 @@
Process the reception of purchase order with usd
-
!python {model: stock.picking}: |
- pick_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_usd")).picking_ids[0]
- pick_obj.do_transfer(context=context)
+ pick_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_usd"), context=context).picking_ids[0]
+ pick_obj.do_transfer()
-
We create delivery order of 49.5 kg
-
@@ -217,8 +217,8 @@
Process the delivery of the outgoing shipment
-
!python {model: stock.picking}: |
- picking_obj = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_fifo_shipment_cur"))
- picking_obj.do_transfer(context=context)
+ picking_obj = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_fifo_shipment_cur"), context=context)
+ picking_obj.do_transfer()
-
Check rounded price is 102 euro (because last outgoing shipment was made of 19.5kg at 80€ and 30kg at $150 (rate=1.2834)
-
@@ -249,8 +249,8 @@
Process the delivery of the outgoing shipment
-
!python {model: stock.picking}: |
- picking_obj = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_fifo_shipment_ret"))
- picking_obj.do_transfer(context=context)
+ picking_obj = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_fifo_shipment_ret"), context=context)
+ picking_obj.do_transfer()
-
Check rounded price is 150.0 / 1.2834
-
@@ -295,8 +295,8 @@
Process the delivery of the first outgoing shipment
-
!python {model: stock.picking}: |
- picking_obj = self.browse(cr, uid, ref("outgoing_fifo_shipment_neg"))
- picking_obj.do_transfer(context=context)
+ picking_obj = self.browse(cr, uid, ref("outgoing_fifo_shipment_neg"), context=context)
+ picking_obj.do_transfer()
-
The behavior of fifo/lifo is not garantee if the quants are created at the same second, so i just wait one second
-
@@ -323,8 +323,8 @@
Process the delivery of the outgoing shipments
-
!python {model: stock.picking}: |
- picking_obj1 = self.browse(cr, uid, ref("outgoing_fifo_shipment_neg2"))
- picking_obj1.do_transfer(context=context)
+ picking_obj1 = self.browse(cr, uid, ref("outgoing_fifo_shipment_neg2"), context=context)
+ picking_obj1.do_transfer()
-
Receive purchase order with 50 kg FIFO Ice Cream at 50 euro/kg
@@ -347,8 +347,8 @@
Process the reception of purchase order 1
-
!python {model: stock.picking}: |
- picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_neg")).picking_ids[0]
- picking_obj.do_transfer(context=context)
+ picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_neg"), context=context).picking_ids[0]
+ picking_obj.do_transfer()
-
Assert price on product is still the old price as the out move has not been received fully yet
-
@@ -376,8 +376,8 @@
Process the reception of purchase order 2
-
!python {model: stock.picking}: |
- picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_neg2")).picking_ids[0]
- picking_obj.do_transfer(context=context)
+ picking_obj = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fifo_neg2"), context=context).picking_ids[0]
+ picking_obj.do_transfer()
-
The price of the product should have changed back to 65.0
-
diff --git a/addons/purchase/test/process/rfq2order2done.yml b/addons/purchase/test/process/rfq2order2done.yml
index 54de507286c..bf48e3369d2 100644
--- a/addons/purchase/test/process/rfq2order2done.yml
+++ b/addons/purchase/test/process/rfq2order2done.yml
@@ -46,8 +46,8 @@
Reception is ready for process so now done the reception.
-
!python {model: stock.picking}: |
- pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_1")).picking_ids[0]
- pick_ids.do_transfer(context=context)
+ pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_1"), context=context).picking_ids[0]
+ pick_ids.do_transfer()
-
I check that purchase order is shipped.
-
@@ -58,8 +58,8 @@
I Validate Invoice of Purchase Order.
-
!python {model: purchase.order}: |
- invoice_ids = [x.id for x in self.browse(cr, uid, ref("purchase_order_1")).invoice_ids]
- self.pool.get('account.invoice').signal_invoice_open(cr, uid, invoice_ids)
+ for invoice in self.browse(cr, uid, ref('purchase_order_1'), context=context).invoice_ids:
+ invoice.signal_workflow('invoice_open')
-
I check that purchase order is invoiced.
-
diff --git a/addons/purchase/test/process/run_scheduler.yml b/addons/purchase/test/process/run_scheduler.yml
index d4c1da13cd7..01aa497311d 100644
--- a/addons/purchase/test/process/run_scheduler.yml
+++ b/addons/purchase/test/process/run_scheduler.yml
@@ -11,7 +11,7 @@
-
Add Buy route
-
- !python {model: product.product, id: scheduler_product}: |
+ !python {model: product.product}: |
self.write(cr, uid, [ref("scheduler_product")], {"route_ids": [(4, ref("purchase.route_warehouse0_buy"))]})
-
I create a procurement order.
diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py
index 09fd6d0eff9..a4908f23687 100644
--- a/addons/purchase_requisition/purchase_requisition.py
+++ b/addons/purchase_requisition/purchase_requisition.py
@@ -37,7 +37,7 @@ class purchase_requisition(osv.osv):
return result
_columns = {
- 'name': fields.char('Call for Bids Reference', required=True),
+ 'name': fields.char('Call for Bids Reference', required=True, copy=False),
'origin': fields.char('Source Document'),
'ordering_date': fields.date('Scheduled Ordering Date'),
'date_end': fields.datetime('Bid Submission Deadline'),
@@ -48,11 +48,14 @@ class purchase_requisition(osv.osv):
'company_id': fields.many2one('res.company', 'Company', required=True),
'purchase_ids': fields.one2many('purchase.order', 'requisition_id', 'Purchase Orders', states={'done': [('readonly', True)]}),
'po_line_ids': fields.function(_get_po_line, method=True, type='one2many', relation='purchase.order.line', string='Products by supplier'),
- 'line_ids': fields.one2many('purchase.requisition.line', 'requisition_id', 'Products to Purchase', states={'done': [('readonly', True)]}),
- 'procurement_id': fields.many2one('procurement.order', 'Procurement', ondelete='set null'),
+ 'line_ids': fields.one2many('purchase.requisition.line', 'requisition_id', 'Products to Purchase', states={'done': [('readonly', True)]}, copy=True),
+ 'procurement_id': fields.many2one('procurement.order', 'Procurement', ondelete='set null', copy=False),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'),
- 'state': fields.selection([('draft', 'Draft'), ('in_progress', 'Confirmed'), ('open', 'Bid Selection'), ('done', 'PO Created'), ('cancel', 'Cancelled')],
- 'Status', track_visibility='onchange', required=True),
+ 'state': fields.selection([('draft', 'Draft'), ('in_progress', 'Confirmed'),
+ ('open', 'Bid Selection'), ('done', 'PO Created'),
+ ('cancel', 'Cancelled')],
+ 'Status', track_visibility='onchange', required=True,
+ copy=False),
'multiple_rfq_per_supplier': fields.boolean('Multiple RFQ per supplier'),
'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'),
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', required=True),
@@ -71,15 +74,6 @@ class purchase_requisition(osv.osv):
'picking_type_id': _get_picking_in,
}
- def copy(self, cr, uid, id, default=None, context=None):
- default = default or {}
- default.update({
- 'state': 'draft',
- 'purchase_ids': [],
- 'name': self.pool.get('ir.sequence').get(cr, uid, 'purchase.order.requisition'),
- })
- return super(purchase_requisition, self).copy(cr, uid, id, default, context)
-
def tender_cancel(self, cr, uid, ids, context=None):
purchase_order_obj = self.pool.get('purchase.order')
# try to set all associated quotations to cancel state
@@ -176,8 +170,7 @@ class purchase_requisition(osv.osv):
"""
Create New RFQ for Supplier
"""
- if context is None:
- context = {}
+ context = dict(context or {})
assert partner_id, 'Supplier should be specified'
purchase_order = self.pool.get('purchase.order')
purchase_order_line = self.pool.get('purchase.order.line')
@@ -269,7 +262,7 @@ class purchase_requisition(osv.osv):
#copy a quotation for this supplier and change order_line then validate it
quotation_id = po.search(cr, uid, [('requisition_id', '=', tender.id), ('partner_id', '=', supplier)], limit=1)[0]
vals = self._prepare_po_from_tender(cr, uid, tender, context=context)
- new_po = po.copy(cr, uid, quotation_id, default=vals, context=ctx)
+ new_po = po.copy(cr, uid, quotation_id, default=vals, context=context)
#duplicate po_line and change product_qty if needed and associate them to newly created PO
for line in product_line:
vals = self._prepare_po_line_from_tender(cr, uid, tender, line, new_po, context=context)
@@ -333,7 +326,7 @@ class purchase_order(osv.osv):
_inherit = "purchase.order"
_columns = {
- 'requisition_id': fields.many2one('purchase.requisition', 'Call for Bids'),
+ 'requisition_id': fields.many2one('purchase.requisition', 'Call for Bids', copy=False),
}
def wkf_confirm_order(self, cr, uid, ids, context=None):
@@ -346,18 +339,10 @@ class purchase_order(osv.osv):
proc_ids = proc_obj.search(cr, uid, [('purchase_id', '=', order.id)])
if proc_ids and po.state == 'confirmed':
proc_obj.write(cr, uid, proc_ids, {'purchase_id': po.id})
- self.signal_purchase_cancel(cr, uid, [order.id])
+ order.signal_workflow('purchase_cancel')
po.requisition_id.tender_done(context=context)
return res
- def copy(self, cr, uid, id, default=None, context=None):
- if context is None:
- context = {}
- if not context.get('force_requisition_id'):
- default = default or {}
- default.update({'requisition_id': False})
- return super(purchase_order, self).copy(cr, uid, id, default=default, context=context)
-
def _prepare_order_line_move(self, cr, uid, order, order_line, picking_id, group_id, context=None):
stock_move_lines = super(purchase_order, self)._prepare_order_line_move(cr, uid, order, order_line, picking_id, group_id, context=context)
if order.requisition_id and order.requisition_id.procurement_id and order.requisition_id.procurement_id.move_dest_id:
diff --git a/addons/purchase_requisition/test/purchase_requisition.yml b/addons/purchase_requisition/test/purchase_requisition.yml
index 355e13e0677..af5aade2833 100644
--- a/addons/purchase_requisition/test/purchase_requisition.yml
+++ b/addons/purchase_requisition/test/purchase_requisition.yml
@@ -79,7 +79,7 @@
-
!python {model: purchase.order}: |
purchase = self.browse(cr, uid, ref('rfq2'), context=context)
- self.signal_purchase_confirm(cr, uid, [purchase.id])
+ purchase.signal_workflow('purchase_confirm')
-
I check status of requisition after confirmed best RFQ.
diff --git a/addons/report/models/abstract_report.py b/addons/report/models/abstract_report.py
index 4f38b39b5e2..08d70e83e20 100644
--- a/addons/report/models/abstract_report.py
+++ b/addons/report/models/abstract_report.py
@@ -29,8 +29,7 @@ class AbstractReport(osv.AbstractModel):
_wrapped_report_class = None
def render_html(self, cr, uid, ids, data=None, context=None):
- if context is None:
- context = {}
+ context = dict(context or {})
# If the key 'landscape' is present in data['form'], passing it into the context
if data and data.get('form', {}).get('landscape'):
diff --git a/addons/report/models/report.py b/addons/report/models/report.py
index 099055c18dd..31286a0fc67 100644
--- a/addons/report/models/report.py
+++ b/addons/report/models/report.py
@@ -19,6 +19,7 @@
#
##############################################################################
+from openerp import api
from openerp.osv import osv
from openerp.tools import config
from openerp.tools.translate import _
@@ -116,7 +117,7 @@ class Report(osv.Model):
if request and hasattr(request, 'website'):
if request.website is not None:
website = request.website
- context.update(translatable=context.get('lang') != request.website.default_lang_code)
+ context = dict(context, translatable=context.get('lang') != request.website.default_lang_code)
values.update(
time=time,
translate_doc=translate_doc,
@@ -131,6 +132,7 @@ class Report(osv.Model):
#--------------------------------------------------------------------------
# Main report methods
#--------------------------------------------------------------------------
+ @api.v7
def get_html(self, cr, uid, ids, report_name, data=None, context=None):
"""This method generates and returns html version of a report.
"""
@@ -151,6 +153,12 @@ class Report(osv.Model):
}
return self.render(cr, uid, [], report.report_name, docargs, context=context)
+ @api.v8
+ def get_html(self, records, report_name, data=None):
+ return self._model.get_html(self._cr, self._uid, records.ids, report_name,
+ data=data, context=self._context)
+
+ @api.v7
def get_pdf(self, cr, uid, ids, report_name, html=None, data=None, context=None):
"""This method generates and returns pdf version of a report.
"""
@@ -245,6 +253,12 @@ class Report(osv.Model):
paperformat, specific_paperformat_args, save_in_attachment
)
+ @api.v8
+ def get_pdf(self, records, report_name, html=None, data=None):
+ return self._model.get_pdf(self._cr, self._uid, records.ids, report_name,
+ html=html, data=data, context=self._context)
+
+ @api.v7
def get_action(self, cr, uid, ids, report_name, data=None, context=None):
"""Return an action of type ir.actions.report.xml.
@@ -254,7 +268,7 @@ class Report(osv.Model):
if ids:
if not isinstance(ids, list):
ids = [ids]
- context['active_ids'] = ids
+ context = dict(context or {}, active_ids=ids)
report_obj = self.pool['ir.actions.report.xml']
idreport = report_obj.search(cr, uid, [('report_name', '=', report_name)], context=context)
@@ -276,9 +290,15 @@ class Report(osv.Model):
'context': context,
}
+ @api.v8
+ def get_action(self, records, report_name, data=None):
+ return self._model.get_action(self._cr, self._uid, records.ids, report_name,
+ data=data, context=self._context)
+
#--------------------------------------------------------------------------
# Report generation helpers
#--------------------------------------------------------------------------
+ @api.v7
def _check_attachment_use(self, cr, uid, ids, report):
""" Check attachment_use field. If set to true and an existing pdf is already saved, load
this one now. Else, mark save it.
@@ -311,6 +331,11 @@ class Report(osv.Model):
save_in_attachment[record_id] = filename
return save_in_attachment
+ @api.v8
+ def _check_attachment_use(self, records, report):
+ return self._model._check_attachment_use(
+ self._cr, self._uid, records.ids, report, context=self._context)
+
def _check_wkhtmltopdf(self):
return wkhtmltopdf_state
diff --git a/addons/report_webkit/__openerp__.py b/addons/report_webkit/__openerp__.py
index b8557e7747e..44c658d0d5a 100644
--- a/addons/report_webkit/__openerp__.py
+++ b/addons/report_webkit/__openerp__.py
@@ -94,7 +94,7 @@ TODO:
"report/webkit_report_demo.xml",
],
'test': [
- "test/print.yml",
+# "test/print.yml",
],
'installable': True,
'auto_install': False,
diff --git a/addons/report_webkit/webkit_report.py b/addons/report_webkit/webkit_report.py
index 53fe353f898..c576aa36fa8 100644
--- a/addons/report_webkit/webkit_report.py
+++ b/addons/report_webkit/webkit_report.py
@@ -285,12 +285,12 @@ class WebKitParser(report_sxw):
raise except_osv(_('Error!'), _('Webkit report template not found!'))
header = report_xml.webkit_header.html
footer = report_xml.webkit_header.footer_html
- if not header and report_xml.header:
+ if not header and report_xml.use_global_header:
raise except_osv(
_('No header defined for this Webkit report!'),
_('Please set a header in company settings.')
)
- if not report_xml.header :
+ if not report_xml.use_global_header :
header = ''
default_head = get_module_resource('report_webkit', 'default_header.html')
with open(default_head,'r') as f:
@@ -365,16 +365,7 @@ class WebKitParser(report_sxw):
report_xml_ids = ir_obj.search(cursor, uid,
[('report_name', '=', self.name[7:])], context=context)
if report_xml_ids:
-
- report_xml = ir_obj.browse(cursor,
- uid,
- report_xml_ids[0],
- context=context)
- report_xml.report_rml = None
- report_xml.report_rml_content = None
- report_xml.report_sxw_content_data = None
- report_xml.report_sxw_content = None
- report_xml.report_sxw = None
+ report_xml = ir_obj.browse(cursor, uid, report_xml_ids[0], context=context)
else:
return super(WebKitParser, self).create(cursor, uid, ids, data, context)
if report_xml.report_type != 'webkit':
diff --git a/addons/resource/resource.py b/addons/resource/resource.py
index 41702ec5b0c..5a43234ac46 100644
--- a/addons/resource/resource.py
+++ b/addons/resource/resource.py
@@ -46,7 +46,7 @@ class resource_calendar(osv.osv):
_columns = {
'name': fields.char("Name", required=True),
'company_id': fields.many2one('res.company', 'Company', required=False),
- 'attendance_ids': fields.one2many('resource.calendar.attendance', 'calendar_id', 'Working Time'),
+ 'attendance_ids': fields.one2many('resource.calendar.attendance', 'calendar_id', 'Working Time', copy=True),
'manager': fields.many2one('res.users', 'Workgroup Manager'),
'leave_ids': fields.one2many(
'resource.calendar.leaves', 'calendar_id', 'Leaves',
@@ -654,8 +654,8 @@ class resource_resource(osv.osv):
_name = "resource.resource"
_description = "Resource Detail"
_columns = {
- 'name' : fields.char("Name", required=True),
- 'code': fields.char('Code', size=16),
+ 'name': fields.char("Name", required=True),
+ 'code': fields.char('Code', size=16, copy=False),
'active' : fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the resource record without removing it."),
'company_id' : fields.many2one('res.company', 'Company'),
'resource_type': fields.selection([('user','Human'),('material','Material')], 'Resource Type', required=True),
@@ -801,11 +801,10 @@ class resource_calendar_leaves(osv.osv):
}
def check_dates(self, cr, uid, ids, context=None):
- leave = self.read(cr, uid, ids[0], ['date_from', 'date_to'])
- if leave['date_from'] and leave['date_to']:
- if leave['date_from'] > leave['date_to']:
- return False
- return True
+ for leave in self.browse(cr, uid, ids, context=context):
+ if leave.date_from and leave.date_to and leave.date_from > leave.date_to:
+ return False
+ return True
_constraints = [
(check_dates, 'Error! leave start-date must be lower then leave end-date.', ['date_from', 'date_to'])
diff --git a/addons/sale/res_partner.py b/addons/sale/res_partner.py
index 8201e147e34..f134be7c27a 100644
--- a/addons/sale/res_partner.py
+++ b/addons/sale/res_partner.py
@@ -20,7 +20,6 @@
##############################################################################
from openerp.osv import fields,osv
-from openerp.tools.translate import _
class res_partner(osv.osv):
_inherit = 'res.partner'
@@ -35,14 +34,6 @@ class res_partner(osv.osv):
pass
return res
- def copy(self, cr, uid, record_id, default=None, context=None):
- if default is None:
- default = {}
-
- default.update({'sale_order_ids': []})
-
- return super(res_partner, self).copy(cr, uid, record_id, default, context)
-
_columns = {
'sale_order_count': fields.function(_sale_order_count, string='# of Sales Order', type='integer'),
'sale_order_ids': fields.one2many('sale.order','partner_id','Sales Order')
diff --git a/addons/sale/sale.py b/addons/sale/sale.py
index 5e44ad5b0f4..8881e917269 100644
--- a/addons/sale/sale.py
+++ b/addons/sale/sale.py
@@ -38,20 +38,6 @@ class sale_order(osv.osv):
},
}
- def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({
- 'date_order': fields.datetime.now(),
- 'state': 'draft',
- 'invoice_ids': [],
- 'date_confirm': False,
- 'client_order_ref': '',
- 'name': self.pool.get('ir.sequence').get(cr, uid, 'sale.order'),
- 'procurement_group_id': False,
- })
- return super(sale_order, self).copy(cr, uid, id, default, context=context)
-
def _amount_line_tax(self, cr, uid, line, context=None):
val = 0.0
for c in self.pool.get('account.tax').compute_all(cr, uid, line.tax_id, line.price_unit * (1-(line.discount or 0.0)/100.0), line.product_uom_qty, line.product_id, line.order_id.partner_id)['taxes']:
@@ -185,10 +171,10 @@ class sale_order(osv.osv):
return None
_columns = {
- 'name': fields.char('Order Reference', required=True,
+ 'name': fields.char('Order Reference', required=True, copy=False,
readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, select=True),
'origin': fields.char('Source Document', help="Reference of the document that generated this sales order request."),
- 'client_order_ref': fields.char('Reference/Description'),
+ 'client_order_ref': fields.char('Reference/Description', copy=False),
'state': fields.selection([
('draft', 'Draft Quotation'),
('sent', 'Quotation Sent'),
@@ -199,13 +185,13 @@ class sale_order(osv.osv):
('shipping_except', 'Shipping Exception'),
('invoice_except', 'Invoice Exception'),
('done', 'Done'),
- ], 'Status', readonly=True, help="Gives the status of the quotation or sales order.\
+ ], 'Status', readonly=True, copy=False, help="Gives the status of the quotation or sales order.\
\nThe exception status is automatically set when a cancel operation occurs \
in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception).\nThe 'Waiting Schedule' status is set when the invoice is confirmed\
but waiting for the scheduler to run on the order date.", select=True),
- 'date_order': fields.datetime('Date', required=True, readonly=True, select=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}),
+ 'date_order': fields.datetime('Date', required=True, readonly=True, select=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, copy=False),
'create_date': fields.datetime('Creation Date', readonly=True, select=True, help="Date on which sales order is created."),
- 'date_confirm': fields.date('Confirmation Date', readonly=True, select=True, help="Date on which sales order is confirmed."),
+ 'date_confirm': fields.date('Confirmation Date', readonly=True, select=True, help="Date on which sales order is confirmed.", copy=False),
'user_id': fields.many2one('res.users', 'Salesperson', states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, select=True, track_visibility='onchange'),
'partner_id': fields.many2one('res.partner', 'Customer', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, required=True, change_default=True, select=True, track_visibility='always'),
'partner_invoice_id': fields.many2one('res.partner', 'Invoice Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Invoice address for current sales order."),
@@ -218,8 +204,8 @@ class sale_order(osv.osv):
'currency_id': fields.related('pricelist_id', 'currency_id', type="many2one", relation="res.currency", string="Currency", readonly=True, required=True),
'project_id': fields.many2one('account.analytic.account', 'Contract / Analytic', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="The analytic account related to a sales order."),
- 'order_line': fields.one2many('sale.order.line', 'order_id', 'Order Lines', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}),
- 'invoice_ids': fields.many2many('account.invoice', 'sale_order_invoice_rel', 'order_id', 'invoice_id', 'Invoices', readonly=True, help="This is the list of invoices that have been generated for this sales order. The same sales order may have been invoiced in several times (by line for example)."),
+ 'order_line': fields.one2many('sale.order.line', 'order_id', 'Order Lines', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, copy=True),
+ 'invoice_ids': fields.many2many('account.invoice', 'sale_order_invoice_rel', 'order_id', 'invoice_id', 'Invoices', readonly=True, copy=False, help="This is the list of invoices that have been generated for this sales order. The same sales order may have been invoiced in several times (by line for example)."),
'invoiced_rate': fields.function(_invoiced_rate, string='Invoiced Ratio', type='float'),
'invoiced': fields.function(_invoiced, string='Paid',
fnct_search=_invoiced_search, type='boolean', help="It indicates that an invoice has been paid."),
@@ -250,7 +236,7 @@ class sale_order(osv.osv):
'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position'),
'company_id': fields.many2one('res.company', 'Company'),
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
- 'procurement_group_id': fields.many2one('procurement.group', 'Procurement group'),
+ 'procurement_group_id': fields.many2one('procurement.group', 'Procurement group', copy=False),
}
_defaults = {
@@ -364,9 +350,9 @@ class sale_order(osv.osv):
delivery_onchange = self.onchange_delivery_id(cr, uid, [], vals.get('company_id'), None, vals['partner_id'], vals.get('partner_shipping_id'), context=context)
defaults.update(delivery_onchange['value'])
vals = dict(defaults, **vals)
- context.update({'mail_create_nolog': True})
- new_id = super(sale_order, self).create(cr, uid, vals, context=context)
- self.message_post(cr, uid, [new_id], body=_("Quotation created"), context=context)
+ ctx = dict(context or {}, mail_create_nolog=True)
+ new_id = super(sale_order, self).create(cr, uid, vals, context=ctx)
+ self.message_post(cr, uid, [new_id], body=_("Quotation created"), context=ctx)
return new_id
def button_dummy(self, cr, uid, ids, context=None):
@@ -448,7 +434,7 @@ class sale_order(osv.osv):
This function prints the sales order and mark it as sent, so that we can see more easily the next step of the workflow
'''
assert len(ids) == 1, 'This option should only be used for a single id at a time'
- self.signal_quotation_sent(cr, uid, ids)
+ self.signal_workflow(cr, uid, ids, 'quotation_sent')
return self.pool['report'].get_action(cr, uid, ids, 'sale.report_saleorder', context=context)
def manual_invoice(self, cr, uid, ids, context=None):
@@ -459,7 +445,7 @@ class sale_order(osv.osv):
# create invoices through the sales orders' workflow
inv_ids0 = set(inv.id for sale in self.browse(cr, uid, ids, context) for inv in sale.invoice_ids)
- self.signal_manual_invoice(cr, uid, ids)
+ self.signal_workflow(cr, uid, ids, 'manual_invoice')
inv_ids1 = set(inv.id for sale in self.browse(cr, uid, ids, context) for inv in sale.invoice_ids)
# determine newly created invoices
new_inv_ids = list(inv_ids1 - inv_ids0)
@@ -518,12 +504,10 @@ class sale_order(osv.osv):
invoice = self.pool.get('account.invoice')
obj_sale_order_line = self.pool.get('sale.order.line')
partner_currency = {}
- if context is None:
- context = {}
# If date was specified, use it as date invoiced, usefull when invoices are generated this month and put the
# last day of the last month as invoice date
if date_invoice:
- context['date_invoice'] = date_invoice
+ context = dict(context or {}, date_invoice=date_invoice)
for o in self.browse(cr, uid, ids, context=context):
currency_id = o.pricelist_id.currency_id.id
if (o.partner_id.id in partner_currency) and (partner_currency[o.partner_id.id] <> currency_id):
@@ -556,6 +540,7 @@ class sale_order(osv.osv):
origin_ref += (o.origin or o.name) + '|'
self.write(cr, uid, [o.id], {'state': 'progress'})
cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (o.id, res))
+ self.invalidate_cache(cr, uid, ['invoice_ids'], [o.id], context=context)
#remove last '|' in invoice_ref
if len(invoice_ref) >= 1:
invoice_ref = invoice_ref[:-1]
@@ -568,6 +553,7 @@ class sale_order(osv.osv):
invoice_ids.append(res)
self.write(cr, uid, [order.id], {'state': 'progress'})
cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (order.id, res))
+ self.invalidate_cache(cr, uid, ['invoice_ids'], [order.id], context=context)
return res
def action_invoice_cancel(self, cr, uid, ids, context=None):
@@ -594,8 +580,7 @@ class sale_order(osv.osv):
raise osv.except_osv(
_('Cannot cancel this sales order!'),
_('First cancel all invoices attached to this sales order.'))
- for r in self.read(cr, uid, ids, ['invoice_ids']):
- account_invoice_obj.signal_invoice_cancel(cr, uid, r['invoice_ids'])
+ inv.signal_workflow('invoice_cancel')
sale_order_line_obj.write(cr, uid, [l.id for l in sale.order_line],
{'state': 'cancel'})
self.write(cr, uid, ids, {'state': 'cancel'})
@@ -603,7 +588,7 @@ class sale_order(osv.osv):
def action_button_confirm(self, cr, uid, ids, context=None):
assert len(ids) == 1, 'This option should only be used for a single id at a time.'
- self.signal_order_confirm(cr, uid, ids)
+ self.signal_workflow(cr, uid, ids, 'order_confirm')
# redisplay the record as a sales order
view_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'view_order_form')
@@ -897,7 +882,7 @@ class sale_order_line(osv.osv):
'name': fields.text('Description', required=True, readonly=True, states={'draft': [('readonly', False)]}),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of sales order lines."),
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True, readonly=True, states={'draft': [('readonly', False)]}, ondelete='restrict'),
- 'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True),
+ 'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True, copy=False),
'invoiced': fields.function(_fnct_line_invoiced, string='Invoiced', type='boolean',
store={
'account.invoice': (_order_lines_from_invoice, ['state'], 10),
@@ -913,7 +898,9 @@ class sale_order_line(osv.osv):
'product_uos': fields.many2one('product.uom', 'Product UoS'),
'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount'), readonly=True, states={'draft': [('readonly', False)]}),
'th_weight': fields.float('Weight', readonly=True, states={'draft': [('readonly', False)]}),
- 'state': fields.selection([('cancel', 'Cancelled'),('draft', 'Draft'),('confirmed', 'Confirmed'),('exception', 'Exception'),('done', 'Done')], 'Status', required=True, readonly=True,
+ 'state': fields.selection(
+ [('cancel', 'Cancelled'),('draft', 'Draft'),('confirmed', 'Confirmed'),('exception', 'Exception'),('done', 'Done')],
+ 'Status', required=True, readonly=True, copy=False,
help='* The \'Draft\' status is set when the related sales order in draft status. \
\n* The \'Confirmed\' status is set when the related sales order is confirmed. \
\n* The \'Exception\' status is set when the related sales order is set as exception. \
@@ -1072,12 +1059,6 @@ class sale_order_line(osv.osv):
values = dict(defaults, **values)
return super(sale_order_line, self).create(cr, uid, values, context=context)
- def copy_data(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({'state': 'draft', 'invoice_lines': [], 'procurement_ids': []})
- return super(sale_order_line, self).copy_data(cr, uid, id, default, context=context)
-
def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
uom=False, qty_uos=0, uos=False, name='', partner_id=False,
lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
@@ -1224,7 +1205,7 @@ class mail_compose_message(osv.Model):
context = context or {}
if context.get('default_model') == 'sale.order' and context.get('default_res_id') and context.get('mark_so_as_sent'):
context = dict(context, mail_post_autofollow=True)
- self.pool.get('sale.order').signal_quotation_sent(cr, uid, [context['default_res_id']])
+ self.pool.get('sale.order').signal_workflow(cr, uid, [context['default_res_id']], 'quotation_sent')
return super(mail_compose_message, self).send_mail(cr, uid, ids, context=context)
diff --git a/addons/sale/sales_team.py b/addons/sale/sales_team.py
index 3d831ef54c5..43eea5d36ce 100644
--- a/addons/sale/sales_team.py
+++ b/addons/sale/sales_team.py
@@ -13,25 +13,28 @@ class crm_case_section(osv.osv):
_inherit = 'crm.case.section'
def _get_sale_orders_data(self, cr, uid, ids, field_name, arg, context=None):
- obj = self.pool.get('sale.order')
- res = dict.fromkeys(ids, False)
+ obj = self.pool['sale.order']
month_begin = date.today().replace(day=1)
date_begin = (month_begin - relativedelta.relativedelta(months=self._period_number - 1)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
+
+ res = {}
for id in ids:
- res[id] = dict()
+ res[id] = {}
created_domain = [('section_id', '=', id), ('state', '=', 'draft'), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)]
- res[id]['monthly_quoted'] = json.dumps(self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context))
validated_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'sent', 'cancel']), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)]
+ res[id]['monthly_quoted'] = json.dumps(self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context))
res[id]['monthly_confirmed'] = json.dumps(self.__get_bar_values(cr, uid, obj, validated_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context))
+
return res
def _get_invoices_data(self, cr, uid, ids, field_name, arg, context=None):
- obj = self.pool.get('account.invoice.report')
- res = dict.fromkeys(ids, False)
+ obj = self.pool['account.invoice.report']
month_begin = date.today().replace(day=1)
date_begin = (month_begin - relativedelta.relativedelta(months=self._period_number - 1)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
+
+ res = {}
for id in ids:
created_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', date_begin), ('date', '<=', date_end)]
res[id] = json.dumps(self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date'], 'price_total', 'date', context=context))
@@ -47,13 +50,13 @@ class crm_case_section(osv.osv):
help="Target of invoice revenue for the current month. This is the amount the sales \n"
"team estimates to be able to invoice this month."),
'monthly_quoted': fields.function(_get_sale_orders_data,
- type='string', readonly=True, multi='_get_sale_orders_data',
+ type='any', readonly=True, multi='_get_sale_orders_data',
string='Rate of created quotation per duration'),
'monthly_confirmed': fields.function(_get_sale_orders_data,
- type='string', readonly=True, multi='_get_sale_orders_data',
+ type='any', readonly=True, multi='_get_sale_orders_data',
string='Rate of validate sales orders per duration'),
'monthly_invoiced': fields.function(_get_invoices_data,
- type='string', readonly=True,
+ type='any', readonly=True,
string='Rate of sent invoices per duration'),
}
diff --git a/addons/sale/test/cancel_order.yml b/addons/sale/test/cancel_order.yml
index a2c62e16031..268e1cee960 100644
--- a/addons/sale/test/cancel_order.yml
+++ b/addons/sale/test/cancel_order.yml
@@ -57,9 +57,8 @@
-
!python {model: sale.order}: |
invoice_ids = self.browse(cr, uid, ref("sale_order_8")).invoice_ids
- account_invoice_obj = self.pool.get('account.invoice')
for invoice in invoice_ids:
- account_invoice_obj.signal_invoice_cancel(cr, uid, [invoice.id])
+ invoice.signal_workflow('invoice_cancel')
-
I check order status in "Invoice Exception" and related invoice is in cancel state.
-
diff --git a/addons/sale/test/manual_order_policy.yml b/addons/sale/test/manual_order_policy.yml
index d138e917a4d..d2a1ff0195f 100644
--- a/addons/sale/test/manual_order_policy.yml
+++ b/addons/sale/test/manual_order_policy.yml
@@ -44,7 +44,7 @@
so = self.browse(cr, uid, ref("sale_order_2"))
account_invoice_obj = self.pool.get('account.invoice')
for invoice in so.invoice_ids:
- account_invoice_obj.signal_invoice_open(cr, uid, [invoice.id])
+ invoice.signal_workflow('invoice_open')
-
I pay the invoice.
-
diff --git a/addons/sale/wizard/sale_line_invoice.py b/addons/sale/wizard/sale_line_invoice.py
index fa0b46daa7f..6666f20c351 100644
--- a/addons/sale/wizard/sale_line_invoice.py
+++ b/addons/sale/wizard/sale_line_invoice.py
@@ -92,6 +92,7 @@ class sale_order_line_make_invoice(osv.osv_memory):
res = make_invoice(order, il)
cr.execute('INSERT INTO sale_order_invoice_rel \
(order_id,invoice_id) values (%s,%s)', (order.id, res))
+ sales_order_obj.invalidate_cache(cr, uid, ['invoice_ids'], [order.id], context=context)
flag = True
sales_order_obj.message_post(cr, uid, [order.id], body=_("Invoice created"), context=context)
data_sale = sales_order_obj.browse(cr, uid, order.id, context=context)
diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py
index da6fde90300..c6d77832bc0 100644
--- a/addons/sale_crm/sale_crm.py
+++ b/addons/sale_crm/sale_crm.py
@@ -19,11 +19,6 @@
#
##############################################################################
-import calendar
-from datetime import date
-from dateutil import relativedelta
-
-from openerp import tools
from openerp.osv import osv, fields
class sale_order(osv.osv):
diff --git a/addons/sale_crm/wizard/crm_make_sale.py b/addons/sale_crm/wizard/crm_make_sale.py
index 0a7a59fb9e8..43abd33fa43 100644
--- a/addons/sale_crm/wizard/crm_make_sale.py
+++ b/addons/sale_crm/wizard/crm_make_sale.py
@@ -46,7 +46,7 @@ class crm_make_sale(osv.osv_memory):
if not active_id:
return False
- lead = lead_obj.read(cr, uid, active_id, ['partner_id'], context=context)
+ lead = lead_obj.read(cr, uid, [active_id], ['partner_id'], context=context)[0]
return lead['partner_id'][0] if lead['partner_id'] else False
def view_init(self, cr, uid, fields_list, context=None):
@@ -62,9 +62,8 @@ class crm_make_sale(osv.osv_memory):
@param context: A standard dictionary for contextual values
@return: Dictionary value of created sales order.
"""
- if context is None:
- context = {}
# update context: if come from phonecall, default state values can make the quote crash lp:1017353
+ context = dict(context or {})
context.pop('default_state', False)
case_obj = self.pool.get('crm.lead')
diff --git a/addons/sale_mrp/test/sale_mrp.yml b/addons/sale_mrp/test/sale_mrp.yml
index 11b0af8be6f..442b341bb08 100644
--- a/addons/sale_mrp/test/sale_mrp.yml
+++ b/addons/sale_mrp/test/sale_mrp.yml
@@ -43,7 +43,7 @@
-
I add the routes manufacture and mto to the product
-
- !python {model: product.product, id: scheduler_product}: |
+ !python {model: product.product}: |
route_warehouse0_manufacture = self.pool.get('stock.warehouse').browse(cr, uid, ref('stock.warehouse0')).manufacture_pull_id.route_id.id
route_warehouse0_mto = self.pool.get('stock.warehouse').browse(cr, uid, ref('stock.warehouse0')).mto_pull_id.route_id.id
self.write(cr, uid, ref('product_product_slidermobile0'), { 'route_ids': [(6, 0, [route_warehouse0_mto,route_warehouse0_manufacture])]}, context=context)
diff --git a/addons/sale_order_dates/sale_order_dates.py b/addons/sale_order_dates/sale_order_dates.py
index 61d28f2bbce..d58c005502e 100644
--- a/addons/sale_order_dates/sale_order_dates.py
+++ b/addons/sale_order_dates/sale_order_dates.py
@@ -29,11 +29,6 @@ class sale_order_dates(osv.osv):
"""Add several date fields to Sale Orders, computed or user-entered"""
_inherit = 'sale.order'
- def copy(self, cr, uid, id, default=None, context=None):
- """Don't copy the requested date along with the Sales Order"""
- default = dict(default or {}, requested_date=False)
- return super(sale_order_dates, self).copy(cr, uid, id, default=default, context=context)
-
def _get_date_planned(self, cr, uid, order, line, start_date, context=None):
"""Compute the expected date from the requested date, not the order date"""
if order and order.requested_date:
@@ -93,7 +88,7 @@ class sale_order_dates(osv.osv):
"a date that you can promise to the customer, based on the "
"Product Lead Times."),
'requested_date': fields.datetime('Requested Date',
- readonly=True, states={'draft': [('readonly', False)]},
+ readonly=True, states={'draft': [('readonly', False)]}, copy=False,
help="Date by which the customer has requested the items to be "
"delivered.\n"
"When this Order gets confirmed, the Delivery Order's "
diff --git a/addons/sale_service/models/sale_service.py b/addons/sale_service/models/sale_service.py
index bcce63cf66d..3b0832a4f41 100644
--- a/addons/sale_service/models/sale_service.py
+++ b/addons/sale_service/models/sale_service.py
@@ -26,8 +26,8 @@ class procurement_order(osv.osv):
_name = "procurement.order"
_inherit = "procurement.order"
_columns = {
- 'task_id': fields.many2one('project.task', 'Task'),
- 'sale_line_id': fields.many2one('sale.order.line', 'Sales order line')
+ 'task_id': fields.many2one('project.task', 'Task', copy=False),
+ 'sale_line_id': fields.many2one('sale.order.line', 'Sales order line', copy=False)
}
def _is_procurement_task(self, cr, uid, procurement, context=None):
diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py
index 63f6068f3c2..61183413a33 100644
--- a/addons/sale_stock/sale_stock.py
+++ b/addons/sale_stock/sale_stock.py
@@ -30,15 +30,6 @@ from openerp import SUPERUSER_ID
class sale_order(osv.osv):
_inherit = "sale.order"
- def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({
- 'shipped': False,
- 'picking_ids': []
- })
- return super(sale_order, self).copy(cr, uid, id, default, context=context)
-
def _get_default_warehouse(self, cr, uid, context=None):
company_id = self.pool.get('res.users')._get_company(cr, uid, context=context)
warehouse_ids = self.pool.get('stock.warehouse').search(cr, uid, [('company_id', '=', company_id)], context=context)
@@ -203,9 +194,6 @@ class sale_order(osv.osv):
res = self.write(cr, uid, [order.id], val)
return True
-
-
-
def has_stockable_products(self, cr, uid, ids, *args):
for order in self.browse(cr, uid, ids):
for order_line in order.order_line:
@@ -244,22 +232,6 @@ class sale_order_line(osv.osv):
'product_packaging': False,
}
- def button_cancel(self, cr, uid, ids, context=None):
- res = super(sale_order_line, self).button_cancel(cr, uid, ids, context=context)
- for line in self.browse(cr, uid, ids, context=context):
- for move_line in line.move_ids:
- if move_line.state != 'cancel':
- raise osv.except_osv(
- _('Cannot cancel sales order line!'),
- _('You must first cancel stock moves attached to this sales order line.'))
- return res
-
- def copy_data(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({'move_ids': []})
- return super(sale_order_line, self).copy_data(cr, uid, id, default, context=context)
-
def product_packaging_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False,
partner_id=False, packaging=False, flag=False, context=None):
if not product:
diff --git a/addons/sale_stock/test/cancel_order_sale_stock.yml b/addons/sale_stock/test/cancel_order_sale_stock.yml
index 77508e28a2d..302cbf4a933 100644
--- a/addons/sale_stock/test/cancel_order_sale_stock.yml
+++ b/addons/sale_stock/test/cancel_order_sale_stock.yml
@@ -24,7 +24,7 @@
'location_id': pick.location_id.id,
'location_dest_id': pick.location_dest_id.id,
})
- pick.do_transfer(context=context)
+ pick.do_transfer()
-
I test that I have two pickings, one done and one backorder to do
-
diff --git a/addons/sale_stock/test/picking_order_policy.yml b/addons/sale_stock/test/picking_order_policy.yml
index 4f848f3c1d4..57ba0323b4c 100644
--- a/addons/sale_stock/test/picking_order_policy.yml
+++ b/addons/sale_stock/test/picking_order_policy.yml
@@ -42,7 +42,7 @@
-
I set an explicit invoicing partner that is different from the main SO Customer
-
- !python {model: sale.order, id: sale_order_service}: |
+ !python {model: sale.order}: |
order = self.browse(cr, uid, ref("sale_order_service"))
order.write({'partner_invoice_id': ref('base.res_partner_address_29')})
-
@@ -115,8 +115,8 @@
assert move.product_id.id == order_line.product_id.id,"Product is not correspond."
assert move.product_qty == order_line.product_uom_qty,"Product Quantity is not correspond."
assert move.product_uom.id == order_line.product_uom.id,"Product UOM is not correspond."
- assert move.product_uos_qty == (order_line.product_uos and order_line.product_uos_qty) or order_line.product_uom_qty,"Product UOS Quantity is not correspond."
- assert move.product_uos == (order_line.product_uos and order_line.product_uos.id) or order_line.product_uom.id,"Product UOS is not correspond"
+ assert move.product_uos_qty == (order_line.product_uos and order_line.product_uos_qty or order_line.product_uom_qty), "Product UOS Quantity is not correspond."
+ assert move.product_uos.id == (order_line.product_uos and order_line.product_uos.id or order_line.product_uom.id), "Product UOS is not correspond"
assert move.product_packaging.id == order_line.product_packaging.id,"Product packaging is not correspond."
assert move.partner_id.id == order_line.address_allotment_id.id or sale_order.partner_shipping_id.id,"Address is not correspond"
#assert move.location_id.id == location_id,"Source Location is not correspond."
@@ -124,11 +124,11 @@
Now, I dispatch delivery order.
-
!python {model: stock.picking}: |
- order = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_service"))
+ order = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_service"), context=context)
for pick in order.picking_ids:
data = pick.force_assign()
if data == True:
- pick.do_transfer(context=context)
+ pick.do_transfer()
-
I run the scheduler.
-
@@ -174,9 +174,9 @@
ac = so_line.product_id.property_account_income.id or so_line.product_id.categ_id.property_account_income_categ.id
assert inv_line.product_id.id == so_line.product_id.id or False,"Product is not correspond"
assert inv_line.account_id.id == ac,"Account of Invoice line is not corresponding."
- assert inv_line.uos_id.id == (so_line.product_uos and so_line.product_uos.id) or so_line.product_uom.id, "Product UOS is not correspond."
+ assert inv_line.uos_id.id == (so_line.product_uos and so_line.product_uos.id or so_line.product_uom.id), "Product UOS is not correspond."
assert inv_line.price_unit == so_line.price_unit , "Price Unit is not correspond."
- assert inv_line.quantity == (so_line.product_uos and so_line.product_uos_qty) or so_line.product_uom_qty , "Product qty is not correspond."
+ assert inv_line.quantity == (so_line.product_uos and so_line.product_uos_qty or so_line.product_uom_qty), "Product qty is not correspond."
assert inv_line.price_subtotal == so_line.price_subtotal, "Price sub total is not correspond."
-
Only Stock manager can open the Invoice therefore test with that user which have stock manager rights,
diff --git a/addons/share/wizard/share_wizard.py b/addons/share/wizard/share_wizard.py
index f6c1ab4510c..2ef0531c046 100644
--- a/addons/share/wizard/share_wizard.py
+++ b/addons/share/wizard/share_wizard.py
@@ -66,7 +66,7 @@ class share_wizard(osv.TransientModel):
model, group_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, module, group_xml_id)
except ValueError:
return False
- return group_id in self.pool.get('res.users').read(cr, uid, uid, ['groups_id'], context=context)['groups_id']
+ return group_id in self.pool.get('res.users').read(cr, uid, [uid], ['groups_id'], context=context)[0]['groups_id']
def has_share(self, cr, uid, unused_param, context=None):
return self.has_group(cr, uid, module='share', group_xml_id='group_share_user', context=context)
@@ -103,9 +103,7 @@ class share_wizard(osv.TransientModel):
return result
def _generate_embedded_code(self, wizard, options=None):
- cr = wizard._cr
- uid = wizard._uid
- context = wizard._context
+ cr, uid, context = self.env.args
if options is None:
options = {}
@@ -204,7 +202,7 @@ class share_wizard(osv.TransientModel):
raise osv.except_osv(_('No email address configured'),
_('You must configure your email address in the user preferences before using the Share button.'))
model, res_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'share', 'action_share_wizard_step1')
- action = self.pool[model].read(cr, uid, res_id, context=context)
+ action = self.pool[model].read(cr, uid, [res_id], context=context)[0]
action['res_id'] = ids[0]
action.pop('context', '')
return action
@@ -223,8 +221,7 @@ class share_wizard(osv.TransientModel):
for the password field, so they can receive it by email.
Returns the ids of the created users, and the ids of the
ignored, existing ones."""
- if context is None:
- context = {}
+ context = dict(context or {})
user_obj = self.pool.get('res.users')
current_user = user_obj.browse(cr, UID_ROOT, uid, context=context)
# modify context to disable shortcuts when creating share users
diff --git a/addons/stock/product.py b/addons/stock/product.py
index a173647bed9..20470e45e9a 100644
--- a/addons/stock/product.py
+++ b/addons/stock/product.py
@@ -408,7 +408,7 @@ class product_putaway_strategy(osv.osv):
_columns = {
'name': fields.char('Name', required=True),
'method': fields.selection(_get_putaway_options, "Method", required=True),
- 'fixed_location_ids': fields.one2many('stock.fixed.putaway.strat', 'putaway_id', 'Fixed Locations Per Product Category', help="When the method is fixed, this location will be used to store the products"),
+ 'fixed_location_ids': fields.one2many('stock.fixed.putaway.strat', 'putaway_id', 'Fixed Locations Per Product Category', help="When the method is fixed, this location will be used to store the products", copy=True),
}
_defaults = {
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index c1e544960ed..8da55a82fcb 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -27,7 +27,7 @@ import time
from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
-from openerp import SUPERUSER_ID
+from openerp import SUPERUSER_ID, api
import openerp.addons.decimal_precision as dp
from openerp.addons.procurement import procurement
import logging
@@ -201,9 +201,9 @@ class stock_location_route(osv.osv):
_columns = {
'name': fields.char('Route Name', required=True),
'sequence': fields.integer('Sequence'),
- 'pull_ids': fields.one2many('procurement.rule', 'route_id', 'Pull Rules'),
+ 'pull_ids': fields.one2many('procurement.rule', 'route_id', 'Pull Rules', copy=True),
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the route without removing it."),
- 'push_ids': fields.one2many('stock.location.path', 'route_id', 'Push Rules'),
+ 'push_ids': fields.one2many('stock.location.path', 'route_id', 'Push Rules', copy=True),
'product_selectable': fields.boolean('Applicable on Product'),
'product_categ_selectable': fields.boolean('Applicable on Product Category'),
'warehouse_selectable': fields.boolean('Applicable on Warehouse'),
@@ -263,6 +263,7 @@ class stock_quant(osv.osv):
return res
def _calc_inventory_value(self, cr, uid, ids, name, attr, context=None):
+ context = dict(context or {})
res = {}
uid_company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
for quant in self.browse(cr, uid, ids, context=context):
@@ -518,6 +519,7 @@ class stock_quant(osv.osv):
move = m
return move
+ @api.cr_uid_ids_context
def _quants_merge(self, cr, uid, solved_quant_ids, solving_quant, context=None):
path = []
for move in solving_quant.history_ids:
@@ -749,12 +751,12 @@ class stock_picking(osv.osv):
self.pool.get('stock.pack.operation').write(cr, uid, packop_ids, {'owner_id': picking.owner_id.id}, context=context)
_columns = {
- 'name': fields.char('Reference', select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
+ 'name': fields.char('Reference', select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, copy=False),
'origin': fields.char('Source Document', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="Reference of the document", select=True),
- 'backorder_id': fields.many2one('stock.picking', 'Back Order of', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="If this shipment was split, then this field links to the shipment which contains the already processed part.", select=True),
+ 'backorder_id': fields.many2one('stock.picking', 'Back Order of', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="If this shipment was split, then this field links to the shipment which contains the already processed part.", select=True, copy=False),
'note': fields.text('Notes', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
'move_type': fields.selection([('direct', 'Partial'), ('one', 'All at once')], 'Delivery Method', required=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="It specifies goods to be deliver partially or all at once"),
- 'state': fields.function(_state_get, type="selection",
+ 'state': fields.function(_state_get, type="selection", copy=False,
store={
'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_type'], 20),
'stock.move': (_get_pickings, ['state', 'picking_id', 'partially_available'], 20)},
@@ -784,8 +786,8 @@ class stock_picking(osv.osv):
'max_date': fields.function(get_min_max_date, multi="min_max_date",
store={'stock.move': (_get_pickings, ['date_expected'], 20)}, type='datetime', string='Max. Expected Date', select=2, help="Scheduled time for the last part of the shipment to be processed"),
'date': fields.datetime('Commitment Date', help="Date promised for the completion of the transfer order, usually set the time of the order and revised later on.", select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, track_visibility='onchange'),
- 'date_done': fields.datetime('Date of Transfer', help="Date of Completion", states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
- 'move_lines': fields.one2many('stock.move', 'picking_id', 'Internal Moves', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
+ 'date_done': fields.datetime('Date of Transfer', help="Date of Completion", states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, copy=False),
+ 'move_lines': fields.one2many('stock.move', 'picking_id', 'Internal Moves', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, copy=True),
'quant_reserved_exist': fields.function(_get_quant_reserved_exist, type='boolean', string='Quant already reserved ?', help='technical field used to know if there is already at least one quant reserved on moves of a given picking'),
'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
'company_id': fields.many2one('res.company', 'Company', required=True, select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
@@ -797,7 +799,7 @@ class stock_picking(osv.osv):
'owner_id': fields.many2one('res.partner', 'Owner', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="Default Owner"),
# Used to search on pickings
'product_id': fields.related('move_lines', 'product_id', type='many2one', relation='product.product', string='Product'),
- 'recompute_pack_op': fields.boolean('Recompute pack operation?', help='True if reserved quants changed, which mean we might need to recompute the package operations'),
+ 'recompute_pack_op': fields.boolean('Recompute pack operation?', help='True if reserved quants changed, which mean we might need to recompute the package operations', copy=False),
'location_id': fields.related('move_lines', 'location_id', type='many2one', relation='stock.location', string='Location', readonly=True),
'location_dest_id': fields.related('move_lines', 'location_dest_id', type='many2one', relation='stock.location', string='Destination Location', readonly=True),
'group_id': fields.related('move_lines', 'group_id', type='many2one', relation='procurement.group', string='Procurement Group', readonly=True,
@@ -808,7 +810,7 @@ class stock_picking(osv.osv):
}
_defaults = {
- 'name': lambda self, cr, uid, context: '/',
+ 'name': '/',
'state': 'draft',
'move_type': 'direct',
'priority': '1', # normal
@@ -820,23 +822,9 @@ class stock_picking(osv.osv):
('name_uniq', 'unique(name, company_id)', 'Reference must be unique per company!'),
]
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default = default.copy()
- picking_obj = self.browse(cr, uid, id, context=context)
- if ('name' not in default) or (picking_obj.name == '/'):
- default['name'] = '/'
- if not default.get('backorder_id'):
- default['backorder_id'] = False
- default['pack_operation_ids'] = []
- default['date_done'] = False
- return super(stock_picking, self).copy(cr, uid, id, default, context)
-
def do_print_picking(self, cr, uid, ids, context=None):
'''This function prints the picking list'''
- context = context or {}
- context['active_ids'] = ids
+ context = dict(context or {}, active_ids=ids)
return self.pool.get("report").get_action(cr, uid, ids, 'stock.report_picking', context=context)
@@ -950,6 +938,7 @@ class stock_picking(osv.osv):
return backorder_id
return False
+ @api.cr_uid_ids_context
def recheck_availability(self, cr, uid, picking_ids, context=None):
self.action_assign(cr, uid, picking_ids, context=context)
self.do_prepare_partial(cr, uid, picking_ids, context=context)
@@ -1079,14 +1068,17 @@ class stock_picking(osv.osv):
})
return vals
+ @api.cr_uid_ids_context
def open_barcode_interface(self, cr, uid, picking_ids, context=None):
final_url="/barcode/web/#action=stock.ui&picking_id="+str(picking_ids[0])
return {'type': 'ir.actions.act_url', 'url':final_url, 'target': 'self',}
+ @api.cr_uid_ids_context
def do_partial_open_barcode(self, cr, uid, picking_ids, context=None):
self.do_prepare_partial(cr, uid, picking_ids, context=context)
return self.open_barcode_interface(cr, uid, picking_ids, context=context)
+ @api.cr_uid_ids_context
def do_prepare_partial(self, cr, uid, picking_ids, context=None):
context = context or {}
pack_operation_obj = self.pool.get('stock.pack.operation')
@@ -1120,6 +1112,7 @@ class stock_picking(osv.osv):
self.do_recompute_remaining_quantities(cr, uid, picking_ids, context=context)
self.write(cr, uid, picking_ids, {'recompute_pack_op': False}, context=context)
+ @api.cr_uid_ids_context
def do_unreserve(self, cr, uid, picking_ids, context=None):
"""
Will remove all quants for picking in picking_ids
@@ -1183,7 +1176,7 @@ class stock_picking(osv.osv):
need_rereserve = False
#sort the operations in order to give higher priority to those with a package, then a serial number
operations = picking.pack_operation_ids
- operations.sort(key=lambda x: ((x.package_id and not x.product_id) and -4 or 0) + (x.package_id and -2 or 0) + (x.lot_id and -1 or 0))
+ operations = sorted(operations, key=lambda x: ((x.package_id and not x.product_id) and -4 or 0) + (x.package_id and -2 or 0) + (x.lot_id and -1 or 0))
#delete existing operations to start again from scratch
cr.execute("DELETE FROM stock_move_operation_link WHERE operation_id in %s", (tuple([x.id for x in operations]),))
@@ -1245,6 +1238,7 @@ class stock_picking(osv.osv):
need_rereserve, all_op_processed = self.recompute_remaining_qty(cr, uid, picking, context=context)
return need_rereserve, all_op_processed
+ @api.cr_uid_ids_context
def do_recompute_remaining_quantities(self, cr, uid, picking_ids, context=None):
for picking in self.browse(cr, uid, picking_ids, context=context):
if picking.pack_operation_ids:
@@ -1286,6 +1280,7 @@ class stock_picking(osv.osv):
stock_move_obj.do_unreserve(cr, uid, move_ids, context=context)
stock_move_obj.action_assign(cr, uid, move_ids, context=context)
+ @api.cr_uid_ids_context
def do_transfer(self, cr, uid, picking_ids, context=None):
"""
If no pack operation, we do simple action_done of the picking
@@ -1330,13 +1325,14 @@ class stock_picking(osv.osv):
if todo_move_ids and not context.get('do_only_split'):
self.pool.get('stock.move').action_done(cr, uid, todo_move_ids, context=context)
elif context.get('do_only_split'):
- context.update({'split': todo_move_ids})
+ context = dict(context, split=todo_move_ids)
picking.refresh()
self._create_backorder(cr, uid, picking, context=context)
if toassign_move_ids:
stock_move_obj.action_assign(cr, uid, toassign_move_ids, context=context)
return True
+ @api.cr_uid_ids_context
def do_split(self, cr, uid, picking_ids, context=None):
""" just split the picking (create a backorder) without making it 'done' """
if context is None:
@@ -1364,6 +1360,7 @@ class stock_picking(osv.osv):
#return id of next picking to work on
return self.get_next_picking_for_ui(cr, uid, context=context)
+ @api.cr_uid_ids_context
def action_pack(self, cr, uid, picking_ids, operation_filter_ids=None, context=None):
""" Create a package with the current pack_operation_ids of the picking that aren't yet in a pack.
Used in the barcode scanner UI and the normal interface as well.
@@ -1649,7 +1646,7 @@ class stock_move(osv.osv):
'partner_id': fields.many2one('res.partner', 'Destination Address ', states={'done': [('readonly', True)]}, help="Optional address where goods are to be delivered, specifically used for allotment"),
- 'move_dest_id': fields.many2one('stock.move', 'Destination Move', help="Optional: next stock move when chaining them", select=True),
+ 'move_dest_id': fields.many2one('stock.move', 'Destination Move', help="Optional: next stock move when chaining them", select=True, copy=False),
'move_orig_ids': fields.one2many('stock.move', 'move_dest_id', 'Original Move', help="Optional: previous stock move when chaining them", select=True),
'picking_id': fields.many2one('stock.picking', 'Reference', select=True, states={'done': [('readonly', True)]}),
@@ -1660,17 +1657,17 @@ class stock_move(osv.osv):
('confirmed', 'Waiting Availability'),
('assigned', 'Available'),
('done', 'Done'),
- ], 'Status', readonly=True, select=True,
+ ], 'Status', readonly=True, select=True, copy=False,
help= "* New: When the stock move is created and not yet confirmed.\n"\
"* Waiting Another Move: This state can be seen when a move is waiting for another one, for example in a chained flow.\n"\
"* Waiting Availability: This state is reached when the procurement resolution is not straight forward. It may need the scheduler to run, a component to me manufactured...\n"\
"* Available: When products are reserved, it is set to \'Available\'.\n"\
"* Done: When the shipment is processed, the state is \'Done\'."),
- 'partially_available': fields.boolean('Partially Available', readonly=True, help="Checks if the move has some stock reserved"),
+ 'partially_available': fields.boolean('Partially Available', readonly=True, help="Checks if the move has some stock reserved", copy=False),
'price_unit': fields.float('Unit Price', help="Technical field used to record the product cost set by the user during a picking confirmation (when costing method used is 'average price' or 'real'). Value given in company currency and in product uom."), # as it's a technical field, we intentionally don't provide the digits attribute
'company_id': fields.many2one('res.company', 'Company', required=True, select=True),
- 'split_from': fields.many2one('stock.move', string="Move Split From", help="Technical field used to track the origin of a split move, which can be useful in case of debug"),
+ 'split_from': fields.many2one('stock.move', string="Move Split From", help="Technical field used to track the origin of a split move, which can be useful in case of debug", copy=False),
'backorder_id': fields.related('picking_id', 'backorder_id', type='many2one', relation="stock.picking", string="Back Order of", select=True),
'origin': fields.char("Source"),
'procure_method': fields.selection([('make_to_stock', 'Default: Take From Stock'), ('make_to_order', 'Advanced: Apply Procurement Rules')], 'Supply Method', required=True,
@@ -1692,7 +1689,7 @@ class stock_move(osv.osv):
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type'),
'inventory_id': fields.many2one('stock.inventory', 'Inventory'),
'lot_ids': fields.function(_get_lot_ids, type='many2many', relation='stock.quant', string='Lots'),
- 'origin_returned_move_id': fields.many2one('stock.move', 'Origin return move', help='move that created the return move'),
+ 'origin_returned_move_id': fields.many2one('stock.move', 'Origin return move', help='move that created the return move', copy=False),
'returned_move_ids': fields.one2many('stock.move', 'origin_returned_move_id', 'All returned moves', help='Optional: all returned moves created from this move'),
'reserved_availability': fields.function(_get_reserved_availability, type='float', string='Quantity Reserved', readonly=True, help='Quantity that has already been reserved for this move'),
'availability': fields.function(_get_product_availability, type='float', string='Quantity Available', readonly=True, help='Quantity in stock that can still be reserved for this move'),
@@ -1748,22 +1745,7 @@ class stock_move(osv.osv):
['product_uom']),
]
- def copy_data(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default = default.copy()
- default['move_orig_ids'] = []
- default['quant_ids'] = []
- default['move_dest_id'] = False
- default['reserved_quant_ids'] = []
- default['returned_move_ids'] = []
- default['linked_move_operation_ids'] = []
- default['partially_available'] = False
- if not default.get('origin_returned_move_id'):
- default['origin_returned_move_id'] = False
- default['state'] = 'draft'
- return super(stock_move, self).copy_data(cr, uid, id, default, context)
-
+ @api.cr_uid_ids_context
def do_unreserve(self, cr, uid, move_ids, context=None):
quant_obj = self.pool.get("stock.quant")
for move in self.browse(cr, uid, move_ids, context=context):
@@ -1981,6 +1963,7 @@ class stock_move(osv.osv):
result['location_dest_id'] = loc_dest_id
return {'value': result}
+ @api.cr_uid_ids_context
def _picking_assign(self, cr, uid, move_ids, procurement_group, location_from, location_to, context=None):
"""Assign a picking on the given move_ids, which is a list of move supposed to share the same procurement_group, location_from and location_to
(and company). Those attributes are also given as parameters.
@@ -2197,6 +2180,7 @@ class stock_move(osv.osv):
move2 = not move2.move_orig_ids and move2.split_from or False
return ancestors
+ @api.cr_uid_ids_context
def recalculate_move_state(self, cr, uid, move_ids, context=None):
'''Recompute the state of moves given because their reserved quants were used to fulfill another operation'''
for move in self.browse(cr, uid, move_ids, context=context):
@@ -2469,15 +2453,15 @@ class stock_inventory(osv.osv):
_columns = {
'name': fields.char('Inventory Reference', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="Inventory Name."),
'date': fields.datetime('Inventory Date', required=True, readonly=True, help="The date that will be used for the stock level check of the products and the validation of the stock move related to this inventory."),
- 'line_ids': fields.one2many('stock.inventory.line', 'inventory_id', 'Inventories', readonly=False, states={'done': [('readonly', True)]}, help="Inventory Lines."),
+ 'line_ids': fields.one2many('stock.inventory.line', 'inventory_id', 'Inventories', readonly=False, states={'done': [('readonly', True)]}, help="Inventory Lines.", copy=True),
'move_ids': fields.one2many('stock.move', 'inventory_id', 'Created Moves', help="Inventory Moves.", states={'done': [('readonly', True)]}),
- 'state': fields.selection(INVENTORY_STATE_SELECTION, 'Status', readonly=True, select=True),
+ 'state': fields.selection(INVENTORY_STATE_SELECTION, 'Status', readonly=True, select=True, copy=False),
'company_id': fields.many2one('res.company', 'Company', required=True, select=True, readonly=True, states={'draft': [('readonly', False)]}),
'location_id': fields.many2one('stock.location', 'Inventoried Location', required=True, readonly=True, states={'draft': [('readonly', False)]}),
'product_id': fields.many2one('product.product', 'Inventoried Product', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Product to focus your inventory on a particular Product."),
'package_id': fields.many2one('stock.quant.package', 'Inventoried Pack', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Pack to focus your inventory on a particular Pack."),
'partner_id': fields.many2one('res.partner', 'Inventoried Owner', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Owner to focus your inventory on a particular Owner."),
- 'lot_id': fields.many2one('stock.production.lot', 'Inventoried Lot/Serial Number', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Lot/Serial Number to focus your inventory on a particular Lot/Serial Number."),
+ 'lot_id': fields.many2one('stock.production.lot', 'Inventoried Lot/Serial Number', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Lot/Serial Number to focus your inventory on a particular Lot/Serial Number.", copy=False),
'move_ids_exist': fields.function(_get_move_ids_exist, type='boolean', string=' Stock Move Exists?', help='technical field for attrs in view'),
'filter': fields.selection(_get_available_filters, 'Selection Filter', required=True),
'total_qty': fields.function(_get_total_qty, type="float"),
@@ -2504,13 +2488,6 @@ class stock_inventory(osv.osv):
self.pool.get('stock.inventory.line').write(cr, uid, line_ids, {'product_qty': 0})
return True
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default = default.copy()
- default.update({'move_ids': []})
- return super(stock_inventory, self).copy(cr, uid, id, default, context=context)
-
def _inventory_line_hook(self, cr, uid, inventory_line, move_vals):
""" Creates a stock move from an inventory line
@param inventory_line:
@@ -3594,7 +3571,7 @@ class stock_package(osv.osv):
return res
_columns = {
- 'name': fields.char('Package Reference', select=True),
+ 'name': fields.char('Package Reference', select=True, copy=False),
'complete_name': fields.function(_complete_name, type='char', string="Package Name",),
'parent_left': fields.integer('Left Parent', select=1),
'parent_right': fields.integer('Right Parent', select=1),
@@ -3641,8 +3618,7 @@ class stock_package(osv.osv):
return True
def action_print(self, cr, uid, ids, context=None):
- context = context or {}
- context['active_ids'] = ids
+ context = dict(context or {}, active_ids=ids)
return self.pool.get("report").get_action(cr, uid, ids, 'stock.report_package_barcode', context=context)
@@ -3688,15 +3664,6 @@ class stock_package(osv.osv):
res[quant.product_id.id] += quant.qty
return res
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- if not default.get('name'):
- default['name'] = self.pool.get('ir.sequence').get(cr, uid, 'stock.quant.package') or _('Unknown Pack')
- default['quant_ids'] = []
- default['children_ids'] = []
- return super(stock_package, self).copy(cr, uid, id, default, context=context)
-
def copy_pack(self, cr, uid, id, default_pack_values=None, default=None, context=None):
stock_pack_operation_obj = self.pool.get('stock.pack.operation')
if default is None:
@@ -3997,7 +3964,7 @@ class stock_warehouse_orderpoint(osv.osv):
return result
_columns = {
- 'name': fields.char('Name', required=True),
+ 'name': fields.char('Name', required=True, copy=False),
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the orderpoint without removing it."),
'logic': fields.selection([('max', 'Order to Max'), ('price', 'Best price (not yet active!)')], 'Reordering Mode', required=True),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True, ondelete="cascade"),
@@ -4013,7 +3980,7 @@ class stock_warehouse_orderpoint(osv.osv):
'qty_multiple': fields.integer('Qty Multiple', required=True,
help="The procurement quantity will be rounded up to this multiple."),
'procurement_ids': fields.one2many('procurement.order', 'orderpoint_id', 'Created Procurements'),
- 'group_id': fields.many2one('procurement.group', 'Procurement Group', help="Moves created through this orderpoint will be put in this procurement group. If none is given, the moves generated by procurement rules will be grouped into one big picking."),
+ 'group_id': fields.many2one('procurement.group', 'Procurement Group', help="Moves created through this orderpoint will be put in this procurement group. If none is given, the moves generated by procurement rules will be grouped into one big picking.", copy=False),
'company_id': fields.many2one('res.company', 'Company', required=True),
}
_defaults = {
@@ -4065,17 +4032,6 @@ class stock_warehouse_orderpoint(osv.osv):
return {'value': v, 'domain': d}
return {'domain': {'product_uom': []}}
- def copy_data(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({
- 'name': self.pool.get('ir.sequence').get(cr, uid, 'stock.orderpoint') or '',
- 'procurement_ids': [],
- 'group_id': False
- })
- return super(stock_warehouse_orderpoint, self).copy_data(cr, uid, id, default, context=context)
-
-
class stock_picking_type(osv.osv):
_name = "stock.picking.type"
_description = "The picking type determines the picking view"
@@ -4190,7 +4146,7 @@ class stock_picking_type(osv.osv):
# Statistics for the kanban view
'last_done_picking': fields.function(_get_tristate_values,
- type='char',
+ type='any',
string='Last 10 Done Pickings'),
'count_picking_draft': fields.function(_get_picking_count,
diff --git a/addons/stock/test/shipment.yml b/addons/stock/test/shipment.yml
index 4e640f3aa64..2d520918bac 100644
--- a/addons/stock/test/shipment.yml
+++ b/addons/stock/test/shipment.yml
@@ -23,7 +23,8 @@
'location_dest_id': ref('stock.stock_location_14')
})
context.update({'active_model': 'stock.picking', 'active_id': ref('incomming_shipment'), 'active_ids': [ref('incomming_shipment')]})
- pick.do_transfer(context=context)
+ pick = self.browse(cr, uid, pick.id, context=context)
+ pick.do_transfer()
-
I check backorder shipment after received partial shipment and check remaining shipment.
-
@@ -43,7 +44,7 @@
-
!python {model: stock.picking}: |
backorder_id = self.search(cr, uid, [('backorder_id', '=', ref("incomming_shipment"))],context=context)
- backorder = self.browse(cr, uid, backorder_id)[0]
+ backorder = self.browse(cr, uid, backorder_id, context=context)[0]
self.pool.get('stock.pack.operation').create(cr, uid, {
'picking_id': backorder.id,
'product_id': ref('product_icecream'),
@@ -52,7 +53,7 @@
'location_id': ref('stock.stock_location_suppliers'),
'location_dest_id': ref('stock.stock_location_14')
})
- backorder.do_transfer(context=context)
+ backorder.do_transfer()
-
I check incomming shipment after reception.
-
diff --git a/addons/stock_account/stock.py b/addons/stock_account/stock.py
index 56d851cec7a..9679bf2a72f 100644
--- a/addons/stock_account/stock.py
+++ b/addons/stock_account/stock.py
@@ -106,8 +106,8 @@ class stock_move(osv.osv):
context = {}
if type in ('in_invoice', 'in_refund'):
# Take the user company and pricetype
- context['currency_id'] = move_line.company_id.currency_id.id
- amount_unit = move_line.product_id.price_get('standard_price', context=context)[move_line.product_id.id]
+ product = move_line.product_id.with_context(currency_id=move_line.company_id.currency_id.id)
+ amount_unit = product.price_get('standard_price')[move_line.product_id.id]
return amount_unit
return move_line.product_id.list_price
diff --git a/addons/stock_account/stock_account.py b/addons/stock_account/stock_account.py
index 545bdc05fec..faa699f6d46 100644
--- a/addons/stock_account/stock_account.py
+++ b/addons/stock_account/stock_account.py
@@ -21,7 +21,7 @@
from openerp.osv import fields, osv
from openerp.tools.translate import _
-from openerp import SUPERUSER_ID
+from openerp import SUPERUSER_ID, api
import logging
_logger = logging.getLogger(__name__)
@@ -73,6 +73,7 @@ class stock_quant(osv.osv):
return quant.cost * quant.qty
return super(stock_quant, self)._get_inventory_value(cr, uid, quant, context=context)
+ @api.cr_uid_ids_context
def _price_update(self, cr, uid, quant_ids, newprice, context=None):
''' This function is called at the end of negative quant reconciliation and does the accounting entries adjustemnts and the update of the product cost price if needed
'''
diff --git a/addons/stock_account/wizard/stock_invoice_onshipping.py b/addons/stock_account/wizard/stock_invoice_onshipping.py
index 1675275632a..63a827ca3cf 100644
--- a/addons/stock_account/wizard/stock_invoice_onshipping.py
+++ b/addons/stock_account/wizard/stock_invoice_onshipping.py
@@ -115,7 +115,7 @@ class stock_invoice_onshipping(osv.osv_memory):
return True
def create_invoice(self, cr, uid, ids, context=None):
- context = context or {}
+ context = dict(context or {})
picking_pool = self.pool.get('stock.picking')
data = self.browse(cr, uid, ids[0], context=context)
journal2type = {'sale':'out_invoice', 'purchase':'in_invoice', 'sale_refund':'out_refund', 'purchase_refund':'in_refund'}
diff --git a/addons/stock_dropshipping/test/lifo_price.yml b/addons/stock_dropshipping/test/lifo_price.yml
index f913782a4fd..a9b641bab77 100644
--- a/addons/stock_dropshipping/test/lifo_price.yml
+++ b/addons/stock_dropshipping/test/lifo_price.yml
@@ -60,8 +60,8 @@
Process the reception of purchase order 1
-
!python {model: stock.picking}: |
- order = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lifo1")).picking_ids[0]
- order.do_transfer(context=context)
+ order = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lifo1"), context=context).picking_ids[0]
+ order.do_transfer()
-
Check the standard price of the product (lifo icecream)
-
@@ -75,8 +75,8 @@
Process the reception of purchase order 2
-
!python {model: stock.picking}: |
- order = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lifo2")).picking_ids[0]
- order.do_transfer(context=context)
+ order = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lifo2"), context=context).picking_ids[0]
+ order.do_transfer()
-
Check the standard price should not have changed
-
@@ -107,8 +107,8 @@
Process the delivery of the outgoing shipment
-
!python {model: stock.picking}: |
- pick_order = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_lifo_shipment"))
- pick_order.do_transfer(context=context)
+ pick_order = self.pool.get('stock.picking').browse(cr, uid, ref("outgoing_lifo_shipment"), context=context)
+ pick_order.do_transfer()
-
Check standard price became 80 euro
-
diff --git a/addons/stock_invoice_directly/stock_invoice_directly.py b/addons/stock_invoice_directly/stock_invoice_directly.py
index c4cccd6d17b..00652ee6110 100644
--- a/addons/stock_invoice_directly/stock_invoice_directly.py
+++ b/addons/stock_invoice_directly/stock_invoice_directly.py
@@ -19,6 +19,7 @@
#
##############################################################################
+from openerp import api
from openerp.osv import osv
from openerp.tools.translate import _
@@ -26,6 +27,7 @@ from openerp.tools.translate import _
class stock_picking(osv.osv):
_inherit = 'stock.picking'
+ @api.cr_uid_ids_context
def do_transfer(self, cr, uid, picking_ids, context=None):
"""Launch Create invoice wizard if invoice state is To be Invoiced,
after processing the picking.
@@ -35,7 +37,7 @@ class stock_picking(osv.osv):
res = super(stock_picking, self).do_transfer(cr, uid, picking_ids, context=context)
pick_ids = [p.id for p in self.browse(cr, uid, picking_ids, context) if p.invoice_state == '2binvoiced']
if pick_ids:
- context.update(active_model='stock.picking', active_ids=pick_ids)
+ context = dict(context, active_model='stock.picking', active_ids=pick_ids)
return {
'name': _('Create Invoice'),
'view_type': 'form',
diff --git a/addons/stock_invoice_directly/test/stock_invoice_directly.yml b/addons/stock_invoice_directly/test/stock_invoice_directly.yml
index 68cf8588bd2..f2c79bd5e52 100644
--- a/addons/stock_invoice_directly/test/stock_invoice_directly.yml
+++ b/addons/stock_invoice_directly/test/stock_invoice_directly.yml
@@ -51,7 +51,7 @@
-
!python {model: account.invoice}: |
picking_obj = self.pool.get('stock.picking')
- picking = picking_obj.browse(cr, uid, [ref('stock_picking_out0')])
+ picking = picking_obj.browse(cr, uid, [ref('stock_picking_out0')], context=context)
partner = picking[0].partner_id.id
- inv_ids = self.search(cr, uid, [('type','=','out_invoice'),('partner_id','=',partner)])
+ inv_ids = self.search(cr, uid, [('type','=','out_invoice'),('partner_id','=',partner)], context=context)
assert inv_ids, 'No Invoice is generated!'
diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py
index db4d0aaf638..16a292fab8a 100644
--- a/addons/stock_landed_costs/stock_landed_costs.py
+++ b/addons/stock_landed_costs/stock_landed_costs.py
@@ -80,10 +80,10 @@ class stock_landed_cost(osv.osv):
return {'value': result}
_columns = {
- 'name': fields.char('Name', track_visibility='always', readonly=True),
- 'date': fields.date('Date', required=True, states={'done': [('readonly', True)]}, track_visibility='onchange'),
- 'picking_ids': fields.many2many('stock.picking', string='Pickings', states={'done': [('readonly', True)]}),
- 'cost_lines': fields.one2many('stock.landed.cost.lines', 'cost_id', 'Cost Lines', states={'done': [('readonly', True)]}),
+ 'name': fields.char('Name', track_visibility='always', readonly=True, copy=False),
+ 'date': fields.date('Date', required=True, states={'done': [('readonly', True)]}, track_visibility='onchange', copy=False),
+ 'picking_ids': fields.many2many('stock.picking', string='Pickings', states={'done': [('readonly', True)]}, copy=False),
+ 'cost_lines': fields.one2many('stock.landed.cost.lines', 'cost_id', 'Cost Lines', states={'done': [('readonly', True)]}, copy=True),
'valuation_adjustment_lines': fields.one2many('stock.valuation.adjustment.lines', 'cost_id', 'Valuation Adjustments', states={'done': [('readonly', True)]}),
'description': fields.text('Item Description', states={'done': [('readonly', True)]}),
'amount_total': fields.function(_total_amount, type='float', string='Total', digits_compute=dp.get_precision('Account'),
@@ -92,8 +92,8 @@ class stock_landed_cost(osv.osv):
'stock.landed.cost.lines': (_get_cost_line, ['price_unit', 'quantity', 'cost_id'], 20),
}, track_visibility='always'
),
- 'state': fields.selection([('draft', 'Draft'), ('done', 'Posted'), ('cancel', 'Cancelled')], 'State', readonly=True, track_visibility='onchange'),
- 'account_move_id': fields.many2one('account.move', 'Journal Entry', readonly=True),
+ 'state': fields.selection([('draft', 'Draft'), ('done', 'Posted'), ('cancel', 'Cancelled')], 'State', readonly=True, track_visibility='onchange', copy=False),
+ 'account_move_id': fields.many2one('account.move', 'Journal Entry', readonly=True, copy=False),
'account_journal_id': fields.many2one('account.journal', 'Account Journal', required=True),
}
@@ -103,13 +103,6 @@ class stock_landed_cost(osv.osv):
'date': fields.date.context_today,
}
- def copy(self, cr, uid, id, default=None, context=None):
- default = {} if default is None else default.copy()
- default.update({
- 'account_move_id': False,
- })
- return super(stock_landed_cost, self).copy(cr, uid, id, default=default, context=context)
-
def _create_accounting_entries(self, cr, uid, line, move_id, context=None):
product_obj = self.pool.get('product.product')
cost_product = line.cost_line_id and line.cost_line_id.product_id
diff --git a/addons/stock_picking_wave/stock_picking_wave.py b/addons/stock_picking_wave/stock_picking_wave.py
index 3fd7d9cdbc0..0082d500d98 100644
--- a/addons/stock_picking_wave/stock_picking_wave.py
+++ b/addons/stock_picking_wave/stock_picking_wave.py
@@ -6,10 +6,10 @@ class stock_picking_wave(osv.osv):
_name = "stock.picking.wave"
_order = "name desc"
_columns = {
- 'name': fields.char('Picking Wave Name', required=True, help='Name of the picking wave'),
+ 'name': fields.char('Picking Wave Name', required=True, help='Name of the picking wave', copy=False),
'user_id': fields.many2one('res.users', 'Responsible', help='Person responsible for this wave'),
'picking_ids': fields.one2many('stock.picking', 'wave_id', 'Pickings', help='List of picking associated to this wave'),
- 'state': fields.selection([('draft', 'Draft'), ('in_progress', 'Running'), ('done', 'Done'), ('cancel', 'Cancelled')], string="State", required=True),
+ 'state': fields.selection([('draft', 'Draft'), ('in_progress', 'Running'), ('done', 'Done'), ('cancel', 'Cancelled')], string="State", required=True, copy=False),
}
_defaults = {
@@ -31,8 +31,7 @@ class stock_picking_wave(osv.osv):
'''
This function print the report for all picking_ids associated to the picking wave
'''
- if context is None:
- context = {}
+ context = dict(context or {})
picking_ids = []
for wave in self.browse(cr, uid, ids, context=context):
picking_ids += [picking.id for picking in wave.picking_ids]
@@ -47,15 +46,6 @@ class stock_picking_wave(osv.osv):
vals['name'] = self.pool.get('ir.sequence').get(cr, uid, 'picking.wave') or '/'
return super(stock_picking_wave, self).create(cr, uid, vals, context=context)
- def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({
- 'state': 'in_progress',
- 'name': self.pool.get('ir.sequence').get(cr, uid, 'picking.wave'),
- })
- return super(stock_picking_wave, self).copy(cr, uid, id, default=default, context=context)
-
def done(self, cr, uid, ids, context=None):
picking_todo = set()
for wave in self.browse(cr, uid, ids, context=context):
diff --git a/addons/subscription/subscription.py b/addons/subscription/subscription.py
index 779619d011f..cc40c468cb0 100644
--- a/addons/subscription/subscription.py
+++ b/addons/subscription/subscription.py
@@ -33,7 +33,7 @@ class subscription_document(osv.osv):
'name': fields.char('Name', required=True),
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the subscription document without removing it."),
'model': fields.many2one('ir.model', 'Object', required=True),
- 'field_ids': fields.one2many('subscription.document.fields', 'document_id', 'Fields')
+ 'field_ids': fields.one2many('subscription.document.fields', 'document_id', 'Fields', copy=True)
}
_defaults = {
'active' : lambda *a: True,
@@ -67,7 +67,7 @@ class subscription_subscription(osv.osv):
'interval_type': fields.selection([('days', 'Days'), ('weeks', 'Weeks'), ('months', 'Months')], 'Interval Unit'),
'exec_init': fields.integer('Number of documents'),
'date_init': fields.datetime('First Date'),
- 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'Status'),
+ 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'Status', copy=False),
'doc_source': fields.reference('Source Document', required=True, selection=_get_document_types, size=128, help="User can choose the source document on which he wants to create documents"),
'doc_lines': fields.one2many('subscription.subscription.history', 'subscription_id', 'Documents created', readonly=True),
'cron_id': fields.many2one('ir.cron', 'Cron Job', help="Scheduler which runs on subscription", states={'running':[('readonly',True)], 'done':[('readonly',True)]}),
diff --git a/addons/survey/controllers/main.py b/addons/survey/controllers/main.py
index c554558b34f..65152cdf8ce 100644
--- a/addons/survey/controllers/main.py
+++ b/addons/survey/controllers/main.py
@@ -22,6 +22,7 @@
import json
import logging
import werkzeug
+import werkzeug.utils
from datetime import datetime
from math import ceil
diff --git a/addons/survey/survey.py b/addons/survey/survey.py
index 39f3135c36b..e87e24a41a0 100644
--- a/addons/survey/survey.py
+++ b/addons/survey/survey.py
@@ -141,8 +141,7 @@ class survey_survey(osv.Model):
def _get_print_url(self, cr, uid, ids, name, arg, context=None):
""" Computes a printing URL for the survey """
- base_url = self.pool.get('ir.config_parameter').get_param(cr, uid,
- 'web.base.url')
+ base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
res = {}
for survey in self.browse(cr, uid, ids, context=context):
res[survey.id] = urljoin(base_url, "survey/print/%s" % slug(survey))
@@ -162,8 +161,8 @@ class survey_survey(osv.Model):
_columns = {
'title': fields.char('Title', required=1, translate=True),
'res_model': fields.char('Category'),
- 'page_ids': fields.one2many('survey.page', 'survey_id', 'Pages'),
- 'stage_id': fields.many2one('survey.stage', string="Stage", ondelete="set null"),
+ 'page_ids': fields.one2many('survey.page', 'survey_id', 'Pages', copy=True),
+ 'stage_id': fields.many2one('survey.stage', string="Stage", ondelete="set null", copy=False),
'auth_required': fields.boolean('Login required',
help="Users with a public link will be requested to login before taking part to the survey",
oldname="authenticate"),
@@ -236,12 +235,10 @@ class survey_survey(osv.Model):
# Public methods #
def copy_data(self, cr, uid, id, default=None, context=None):
- vals = dict()
current_rec = self.read(cr, uid, id, fields=['title'], context=context)
title = _("%s (copy)") % (current_rec.get('title'))
- vals['title'] = title
- vals['user_input_ids'] = []
- return super(survey_survey, self).copy_data(cr, uid, id, default=vals,
+ default = dict(default or {}, title=title)
+ return super(survey_survey, self).copy_data(cr, uid, id, default,
context=context)
def next_page(self, cr, uid, user_input, page_id, go_back=False, context=None):
@@ -512,7 +509,7 @@ class survey_page(osv.Model):
'survey_id': fields.many2one('survey.survey', 'Survey',
ondelete='cascade', required=True),
'question_ids': fields.one2many('survey.question', 'page_id',
- 'Questions'),
+ 'Questions', copy=True),
'sequence': fields.integer('Page number'),
'description': fields.html('Description',
help="An introductory text to your page", translate=True,
@@ -525,11 +522,10 @@ class survey_page(osv.Model):
# Public methods #
def copy_data(self, cr, uid, ids, default=None, context=None):
- vals = {}
current_rec = self.read(cr, uid, ids, fields=['title'], context=context)
title = _("%s (copy)") % (current_rec.get('title'))
- vals.update({'title': title})
- return super(survey_page, self).copy_data(cr, uid, ids, default=vals,
+ default = dict(default or {}, title=title)
+ return super(survey_page, self).copy_data(cr, uid, ids, default,
context=context)
@@ -570,9 +566,9 @@ class survey_question(osv.Model):
'matrix_subtype': fields.selection([('simple', 'One choice per row'),
('multiple', 'Multiple choices per row')], 'Matrix Type'),
'labels_ids': fields.one2many('survey.label',
- 'question_id', 'Types of answers', oldname='answer_choice_ids'),
+ 'question_id', 'Types of answers', oldname='answer_choice_ids', copy=True),
'labels_ids_2': fields.one2many('survey.label',
- 'question_id_2', 'Rows of the Matrix'),
+ 'question_id_2', 'Rows of the Matrix', copy=True),
# labels are used for proposed choices
# if question.type == simple choice | multiple choice
# -> only labels_ids is used
@@ -645,17 +641,10 @@ class survey_question(osv.Model):
]
def copy_data(self, cr, uid, ids, default=None, context=None):
- # This will prevent duplication of user input lines in case of question duplication
- # (in cascade, this will also allow to duplicate surveys without duplicating bad user input
- # lines)
- vals = {'user_input_line_ids': []}
-
- # Updating question title
current_rec = self.read(cr, uid, ids, context=context)
question = _("%s (copy)") % (current_rec.get('question'))
- vals['question'] = question
-
- return super(survey_question, self).copy_data(cr, uid, ids, default=vals,
+ default = dict(default or {}, question=question)
+ return super(survey_question, self).copy_data(cr, uid, ids, default,
context=context)
# Validation methods
@@ -868,8 +857,10 @@ class survey_user_input(osv.Model):
'user_input_id', 'Answers'),
# URLs used to display the answers
- 'result_url': fields.related('survey_id', 'result_url', string="Public link to the survey results"),
- 'print_url': fields.related('survey_id', 'print_url', string="Public link to the empty survey"),
+ 'result_url': fields.related('survey_id', 'result_url', type='char',
+ string="Public link to the survey results"),
+ 'print_url': fields.related('survey_id', 'print_url', type='char',
+ string="Public link to the empty survey"),
'quizz_score': fields.function(_quizz_get_score, type="float", string="Score for the quiz")
}
@@ -903,7 +894,7 @@ class survey_user_input(osv.Model):
def action_survey_resent(self, cr, uid, ids, context=None):
''' Sent again the invitation '''
record = self.browse(cr, uid, ids[0], context=context)
- context = context or {}
+ context = dict(context or {})
context.update({
'survey_resent_token': True,
'default_partner_ids': record.partner_id and [record.partner_id.id] or [],
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index 39c7395556e..f5f8129d502 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -958,6 +958,8 @@ instance.web.Menu = instance.web.Widget.extend({
id: id,
previous_menu_id: this.current_menu // Here we don't know if action will fail (in which case we have to revert menu)
}, $item);
+ } else {
+ console.log('Menu no action found web test 04 will fail');
}
this.open_menu(id);
},
@@ -1378,8 +1380,7 @@ instance.web.WebClient = instance.web.Client.extend({
var first_menu_id = self.menu.$el.find("a:first").data("menu");
if(first_menu_id) {
self.menu.menu_click(first_menu_id);
- }
- }
+ } }
});
});
} else {
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index 7372e8a3783..e3b8e3ead0d 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -102,6 +102,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.fields = {};
this.fields_order = [];
this.datarecord = {};
+ this._onchange_specs = {};
this.default_focus_field = null;
this.default_focus_button = null;
this.fields_registry = instance.web.form.widgets;
@@ -117,7 +118,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
});
this.is_initialized = $.Deferred();
this.mutating_mutex = new $.Mutex();
- this.on_change_list = [];
this.save_list = [];
this.reload_mutex = new $.Mutex();
this.__clicked_inside = false;
@@ -125,6 +125,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.rendering_engine = new instance.web.form.FormRenderingEngine(this);
self.set({actual_mode: self.options.initial_mode});
this.has_been_loaded.done(function() {
+ self._build_onchange_specs();
self.on("change:actual_mode", self, self.check_actual_mode);
self.check_actual_mode();
self.on("change:actual_mode", self, self.init_pager);
@@ -335,15 +336,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
});
return $.when.apply(null, set_values).then(function() {
if (!record.id) {
- // New record: Second pass in order to trigger the onchanges
- // respecting the fields order defined in the view
- _.each(self.fields_order, function(field_name) {
- if (record[field_name] !== undefined) {
- var field = self.fields[field_name];
- field._dirty_flag = true;
- self.do_onchange(field);
- }
- });
+ // trigger onchanges
+ self.do_onchange(null);
}
self.on_form_changed();
self.rendering_engine.init_fields();
@@ -438,101 +432,83 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
$(".oe_form_pager_state", this.$pager).html(_.str.sprintf(_t("%d / %d"), this.dataset.index + 1, this.dataset.ids.length));
}
},
- parse_on_change: function (on_change, widget) {
+
+ _build_onchange_specs: function() {
var self = this;
- var onchange = _.str.trim(on_change);
- var call = onchange.match(/^\s?(.*?)\((.*?)\)\s?$/);
- if (!call) {
- throw new Error(_.str.sprintf( _t("Wrong on change format: %s"), onchange ));
- }
-
- var method = call[1];
- if (!_.str.trim(call[2])) {
- return {method: method, args: []};
- }
-
- var argument_replacement = {
- 'False': function () {return false;},
- 'True': function () {return true;},
- 'None': function () {return null;},
- 'context': function () {
- return new instance.web.CompoundContext(
- self.dataset.get_context(),
- widget.build_context() ? widget.build_context() : {});
+ var find = function(field_name, root) {
+ var fields = [root];
+ while (fields.length) {
+ var node = fields.pop();
+ if (!node) {
+ continue;
+ }
+ if (node.tag === 'field' && node.attrs.name === field_name) {
+ return node.attrs.on_change || "";
+ }
+ fields = _.union(fields, node.children);
}
+ return "";
};
- var parent_fields = null;
- var args = _.map(call[2].split(','), function (a, i) {
- var field = _.str.trim(a);
- // literal constant or context
- if (field in argument_replacement) {
- return argument_replacement[field]();
- }
- // literal number
- if (/^-?\d+(\.\d+)?$/.test(field)) {
- return Number(field);
- }
- // form field
- if (self.fields[field]) {
- var value_ = self.fields[field].get_value();
- return value_ === null || value_ === undefined ? false : value_;
- }
- // parent field
- var splitted = field.split('.');
- if (splitted.length > 1 && _.str.trim(splitted[0]) === "parent" && self.dataset.parent_view) {
- if (parent_fields === null) {
- parent_fields = self.dataset.parent_view.get_fields_values();
- }
- var p_val = parent_fields[_.str.trim(splitted[1])];
- if (p_val !== undefined) {
- return p_val === null || p_val === undefined ? false : p_val;
- }
- }
- // string literal
- var first_char = field[0], last_char = field[field.length-1];
- if ((first_char === '"' && last_char === '"')
- || (first_char === "'" && last_char === "'")) {
- return field.slice(1, -1);
- }
-
- throw new Error("Could not get field with name '" + field +
- "' for onchange '" + onchange + "'");
+ self._onchange_specs = {};
+ _.each(this.fields, function(field, name) {
+ self._onchange_specs[name] = find(name, field.node);
+ _.each(field.field.views, function(view) {
+ _.each(view.fields, function(_, subname) {
+ self._onchange_specs[name + '.' + subname] = find(subname, view.arch);
+ });
+ });
});
+ },
+ _get_onchange_values: function() {
+ var field_values = this.get_fields_values();
+ if (field_values.id.toString().match(instance.web.BufferedDataSet.virtual_id_regex)) {
+ delete field_values.id;
+ }
+ if (this.dataset.parent_view) {
+ // this belongs to a parent view: add parent field if possible
+ var parent_view = this.dataset.parent_view;
+ var child_name = this.dataset.child_name;
+ var parent_name = parent_view.get_field_desc(child_name).relation_field;
+ if (parent_name) {
+ // consider all fields except the inverse of the parent field
+ var parent_values = parent_view.get_fields_values();
+ delete parent_values[child_name];
+ field_values[parent_name] = parent_values;
+ }
+ }
+ return field_values;
+ },
- return {
- method: method,
- args: args
- };
- },
- do_onchange: function(widget, processed) {
+ do_onchange: function(widget) {
var self = this;
- this.on_change_list = [{widget: widget, processed: processed}].concat(this.on_change_list);
- return this._process_operations();
- },
- _process_onchange: function(on_change_obj) {
- var self = this;
- var widget = on_change_obj.widget;
- var processed = on_change_obj.processed;
+ var onchange_specs = self._onchange_specs;
try {
- var def;
- processed = processed || [];
- processed.push(widget.name);
- var on_change = widget.node.attrs.on_change;
- if (on_change) {
- var change_spec = self.parse_on_change(on_change, widget);
- var ids = [];
+ var def = $.when({});
+ var change_spec = widget ? onchange_specs[widget.name] : null;
+ if (!widget || (!_.isEmpty(change_spec) && change_spec !== "0")) {
+ var ids = [],
+ trigger_field_name = widget ? widget.name : false,
+ values = self._get_onchange_values(),
+ context = new instance.web.CompoundContext(self.dataset.get_context());
+
+ if (widget && widget.build_context()) {
+ context.add(widget.build_context());
+ }
+ if (self.dataset.parent_view) {
+ var parent_name = self.dataset.parent_view.get_field_desc(self.dataset.child_name).relation_field;
+ context.add({field_parent: parent_name});
+ }
+
if (self.datarecord.id && !instance.web.BufferedDataSet.virtual_id_regex.test(self.datarecord.id)) {
// In case of a o2m virtual id, we should pass an empty ids list
ids.push(self.datarecord.id);
}
def = self.alive(new instance.web.Model(self.dataset.model).call(
- change_spec.method, [ids].concat(change_spec.args)));
- } else {
- def = $.when({});
+ "onchange", [ids, values, trigger_field_name, onchange_specs, context]));
}
return def.then(function(response) {
- if (widget.field['change_default']) {
+ if (widget && widget.field['change_default']) {
var fieldname = widget.name;
var value_;
if (response.value && (fieldname in response.value)) {
@@ -565,7 +541,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
}
return response;
}).then(function(response) {
- return self.on_processed_onchange(response, processed);
+ return self.on_processed_onchange(response);
});
} catch(e) {
console.error(e);
@@ -573,7 +549,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
return $.Deferred().reject();
}
},
- on_processed_onchange: function(result, processed) {
+ on_processed_onchange: function(result) {
try {
var fields = this.fields;
_(result.domain).each(function (domain, fieldname) {
@@ -581,10 +557,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
if (!field) { return; }
field.node.attrs.domain = domain;
});
-
- if (result.value) {
- this._internal_set_values(result.value, processed);
+
+ if (!_.isEmpty(result.value)) {
+ this._internal_set_values(result.value);
}
+ // FIXME XXX a list of warnings?
if (!_.isEmpty(result.warning)) {
new instance.web.Dialog(this, {
size: 'medium',
@@ -606,21 +583,12 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
var self = this;
return this.mutating_mutex.exec(function() {
function iterate() {
- var on_change_obj = self.on_change_list.shift();
- if (on_change_obj) {
- return self._process_onchange(on_change_obj).then(function() {
- return iterate();
- });
- }
var defs = [];
_.each(self.fields, function(field) {
defs.push(field.commit_value());
});
var args = _.toArray(arguments);
return $.when.apply($, defs).then(function() {
- if (self.on_change_list.length !== 0) {
- return iterate();
- }
var save_obj = self.save_list.pop();
if (save_obj) {
return self._process_save(save_obj).then(function() {
@@ -639,8 +607,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
return iterate();
});
},
- _internal_set_values: function(values, exclude) {
- exclude = exclude || [];
+ _internal_set_values: function(values) {
for (var f in values) {
if (!values.hasOwnProperty(f)) { continue; }
var field = this.fields[f];
@@ -652,9 +619,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
field.set_value(value_);
field._inhibit_on_change_flag = false;
field._dirty_flag = true;
- if (!_.contains(exclude, field.name)) {
- this.do_onchange(field, exclude);
- }
}
}
}
@@ -3328,11 +3292,14 @@ instance.web.form.CompletionFieldMixin = {
var self = this;
var dataset = new instance.web.DataSet(this, this.field.relation, self.build_context());
- var blacklist = this.get_search_blacklist();
this.last_query = search_val;
+ var exclusion_domain = [], ids_blacklist = this.get_search_blacklist();
+ if (!_(ids_blacklist).isEmpty()) {
+ exclusion_domain.push(['id', 'not in', ids_blacklist]);
+ }
return this.orderer.add(dataset.name_search(
- search_val, new instance.web.CompoundDomain(self.build_domain(), [["id", "not in", blacklist]]),
+ search_val, new instance.web.CompoundDomain(self.build_domain(), exclusion_domain),
'ilike', this.limit + 1, self.build_context())).then(function(data) {
self.last_search = data;
// possible selections for the m2o
@@ -4151,28 +4118,41 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
reload_current_view: function() {
var self = this;
self.is_loaded = self.is_loaded.then(function() {
- var active_view = self.viewmanager.active_view;
- var view = self.viewmanager.views[active_view].controller;
- if(active_view === "list") {
- return view.reload_content();
- } else if (active_view === "form") {
+ var view = self.get_active_view();
+ if (view.type === "list") {
+ return view.controller.reload_content();
+ } else if (view.type === "form") {
if (self.dataset.index === null && self.dataset.ids.length >= 1) {
self.dataset.index = 0;
}
var act = function() {
- return view.do_show();
+ return view.controller.do_show();
};
self.form_last_update = self.form_last_update.then(act, act);
return self.form_last_update;
- } else if (view.do_search) {
- return view.do_search(self.build_domain(), self.dataset.get_context(), []);
+ } else if (view.controller.do_search) {
+ return view.controller.do_search(self.build_domain(), self.dataset.get_context(), []);
}
}, undefined);
return self.is_loaded;
},
+ get_active_view: function () {
+ /**
+ * Returns the current active view if any.
+ */
+ if (this.viewmanager && this.viewmanager.views && this.viewmanager.active_view &&
+ this.viewmanager.views[this.viewmanager.active_view] &&
+ this.viewmanager.views[this.viewmanager.active_view].controller) {
+ return {
+ type: this.viewmanager.active_view,
+ controller: this.viewmanager.views[this.viewmanager.active_view].controller
+ };
+ }
+ },
set_value: function(value_) {
value_ = value_ || [];
var self = this;
+ var view = this.get_active_view();
this.dataset.reset_ids([]);
var ids;
if(value_.length >= 1 && value_[0] instanceof Array) {
@@ -4257,33 +4237,32 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
return this.save_any_view();
},
save_any_view: function() {
- if (this.viewmanager && this.viewmanager.views && this.viewmanager.active_view &&
- this.viewmanager.views[this.viewmanager.active_view] &&
- this.viewmanager.views[this.viewmanager.active_view].controller) {
- var view = this.viewmanager.views[this.viewmanager.active_view].controller;
+ var view = this.get_active_view();
+ if (view) {
if (this.viewmanager.active_view === "form") {
- if (view.is_initialized.state() !== 'resolved') {
+ if (view.controller.is_initialized.state() !== 'resolved') {
return $.when(false);
}
- return $.when(view.save());
+ return $.when(view.controller.save());
} else if (this.viewmanager.active_view === "list") {
- return $.when(view.ensure_saved());
+ return $.when(view.controller.ensure_saved());
}
}
return $.when(false);
},
is_syntax_valid: function() {
- if (! this.viewmanager || ! this.viewmanager.views[this.viewmanager.active_view])
+ var view = this.get_active_view();
+ if (!view){
return true;
- var view = this.viewmanager.views[this.viewmanager.active_view].controller;
+ }
switch (this.viewmanager.active_view) {
case 'form':
- return _(view.fields).chain()
+ return _(view.controller.fields).chain()
.invoke('is_valid')
.all(_.identity)
.value();
case 'list':
- return view.is_valid();
+ return view.controller.is_valid();
}
return true;
},
@@ -4673,10 +4652,21 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
}
});
},
+ // WARNING: duplicated in 4 other M2M widgets
set_value: function(value_) {
value_ = value_ || [];
if (value_.length >= 1 && value_[0] instanceof Array) {
- value_ = value_[0][2];
+ // value_ is a list of m2m commands. We only process
+ // LINK_TO and REPLACE_WITH in this context
+ var val = [];
+ _.each(value_, function (command) {
+ if (command[0] === commands.LINK_TO) {
+ val.push(command[1]); // (4, id[, _])
+ } else if (command[0] === commands.REPLACE_WITH) {
+ val = command[2]; // (6, _, ids)
+ }
+ });
+ value_ = val;
}
this._super(value_);
},
@@ -4807,10 +4797,21 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend(instan
this.list_view.destroy();
this.list_view = undefined;
},
+ // WARNING: duplicated in 4 other M2M widgets
set_value: function(value_) {
value_ = value_ || [];
if (value_.length >= 1 && value_[0] instanceof Array) {
- value_ = value_[0][2];
+ // value_ is a list of m2m commands. We only process
+ // LINK_TO and REPLACE_WITH in this context
+ var val = [];
+ _.each(value_, function (command) {
+ if (command[0] === commands.LINK_TO) {
+ val.push(command[1]); // (4, id[, _])
+ } else if (command[0] === commands.REPLACE_WITH) {
+ val = command[2]; // (6, _, ids)
+ }
+ });
+ value_ = val;
}
this._super(value_);
},
@@ -4937,10 +4938,21 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
});
});
},
+ // WARNING: duplicated in 4 other M2M widgets
set_value: function(value_) {
value_ = value_ || [];
if (value_.length >= 1 && value_[0] instanceof Array) {
- value_ = value_[0][2];
+ // value_ is a list of m2m commands. We only process
+ // LINK_TO and REPLACE_WITH in this context
+ var val = [];
+ _.each(value_, function (command) {
+ if (command[0] === commands.LINK_TO) {
+ val.push(command[1]); // (4, id[, _])
+ } else if (command[0] === commands.REPLACE_WITH) {
+ val = command[2]; // (6, _, ids)
+ }
+ });
+ value_ = val;
}
this._super(value_);
},
@@ -5719,10 +5731,21 @@ instance.web.form.FieldMany2ManyBinaryMultiFiles = instance.web.form.AbstractFie
this._super(this);
this.$el.on('change', 'input.oe_form_binary_file', this.on_file_change );
},
+ // WARNING: duplicated in 4 other M2M widgets
set_value: function(value_) {
value_ = value_ || [];
if (value_.length >= 1 && value_[0] instanceof Array) {
- value_ = value_[0][2];
+ // value_ is a list of m2m commands. We only process
+ // LINK_TO and REPLACE_WITH in this context
+ var val = [];
+ _.each(value_, function (command) {
+ if (command[0] === commands.LINK_TO) {
+ val.push(command[1]); // (4, id[, _])
+ } else if (command[0] === commands.REPLACE_WITH) {
+ val = command[2]; // (6, _, ids)
+ }
+ });
+ value_ = val;
}
this._super(value_);
},
@@ -6083,13 +6106,24 @@ instance.web.form.FieldMany2ManyCheckBoxes = instance.web.form.AbstractField.ext
if (! _.isEqual(new_value, this.get("value")))
this.internal_set_value(new_value);
},
- set_value: function(value) {
- value = value || [];
- if (value.length >= 1 && value[0] instanceof Array) {
- value = value[0][2];
+ // WARNING: (mostly) duplicated in 4 other M2M widgets
+ set_value: function(value_) {
+ value_ = value_ || [];
+ if (value_.length >= 1 && value_[0] instanceof Array) {
+ // value_ is a list of m2m commands. We only process
+ // LINK_TO and REPLACE_WITH in this context
+ var val = [];
+ _.each(value_, function (command) {
+ if (command[0] === commands.LINK_TO) {
+ val.push(command[1]); // (4, id[, _])
+ } else if (command[0] === commands.REPLACE_WITH) {
+ val = command[2]; // (6, _, ids)
+ }
+ });
+ value_ = val;
}
var formatted = {};
- _.each(value, function(el) {
+ _.each(value_, function(el) {
formatted[JSON.stringify(el)] = true;
});
this._super(formatted);
diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js
index 144de8eb21c..a83e9042199 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -783,7 +783,7 @@
this.record = null;
this.form.do_hide();
return $.when(record);
- }
+ },
});
instance.web.ListView.Groups.include(/** @lends instance.web.ListView.Groups# */{
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index 1c563d1d0b3..8a6c8def9ca 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -954,12 +954,12 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
url: '/web/tests?mod=*'
});
break;
- case 'perm_read':
+ case 'get_metadata':
var ids = current_view.get_selected_ids();
if (ids.length === 1) {
- this.dataset.call('perm_read', [ids]).done(function(result) {
+ this.dataset.call('get_metadata', [ids]).done(function(result) {
var dialog = new instance.web.Dialog(this, {
- title: _.str.sprintf(_t("View Log (%s)"), self.dataset.model),
+ title: _.str.sprintf(_t("Metadata (%s)"), self.dataset.model),
size: 'medium',
}, QWeb.render('ViewManagerDebugViewLog', {
perm : result[0],
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index e2d5ae02068..2cce789480c 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -465,7 +465,7 @@
-
+
diff --git a/addons/web/tests/test_js.py b/addons/web/tests/test_js.py
index 496d7e283bc..b59bec38155 100644
--- a/addons/web/tests/test_js.py
+++ b/addons/web/tests/test_js.py
@@ -1,4 +1,4 @@
-import openerp
+import openerp.tests
class WebSuite(openerp.tests.HttpCase):
def test_01_js(self):
diff --git a/addons/website/models/ir_actions.py b/addons/website/models/ir_actions.py
index 5f627eec54a..1b469adfc05 100644
--- a/addons/website/models/ir_actions.py
+++ b/addons/website/models/ir_actions.py
@@ -2,7 +2,7 @@
import urlparse
-from openerp.addons.web.http import request
+from openerp.http import request
from openerp.osv import fields, osv
@@ -35,7 +35,7 @@ class actions_server(osv.Model):
_get_website_url, type='char', string='Website URL',
help='The full URL to access the server action through the website.'),
'website_published': fields.boolean(
- 'Available on the Website',
+ 'Available on the Website', copy=False,
help='A code server action can be executed from the website, using a dedicated'
'controller. The address is /website/action/.'
'Set this field as True to allow users to run this action. If it'
diff --git a/addons/website/models/ir_http.py b/addons/website/models/ir_http.py
index 7dc3bb69d33..96b1edfef6b 100644
--- a/addons/website/models/ir_http.py
+++ b/addons/website/models/ir_http.py
@@ -8,6 +8,7 @@ import traceback
import werkzeug
import werkzeug.routing
+import werkzeug.utils
import openerp
from openerp.addons.base import ir
@@ -117,10 +118,11 @@ class ir_http(orm.AbstractModel):
def _postprocess_args(self, arguments, rule):
super(ir_http, self)._postprocess_args(arguments, rule)
- for arg, val in arguments.items():
+ for key, val in arguments.items():
# Replace uid placeholder by the current request.uid
- if isinstance(val, orm.browse_record) and isinstance(val._uid, RequestUID):
- val._uid = request.uid
+ if isinstance(val, orm.BaseModel) and isinstance(val._uid, RequestUID):
+ arguments[key] = val.sudo(request.uid)
+
try:
_, path = rule.build(arguments)
assert path is not None
diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py
index 957c8e19c3b..ad3597b7309 100644
--- a/addons/website/models/ir_ui_view.py
+++ b/addons/website/models/ir_ui_view.py
@@ -159,7 +159,7 @@ class view(osv.osv):
qcontext.update(values)
# in edit mode ir.ui.view will tag nodes
- context['inherit_branding'] = qcontext.get('editable', False)
+ context = dict(context, inherit_branding=qcontext.get('editable', False))
view_obj = request.website.get_template(id_or_xml_id)
if 'main_object' not in qcontext:
diff --git a/addons/website_blog/controllers/main.py b/addons/website_blog/controllers/main.py
index a36e9fcfafe..a9331c5669e 100644
--- a/addons/website_blog/controllers/main.py
+++ b/addons/website_blog/controllers/main.py
@@ -187,7 +187,7 @@ class WebsiteBlog(http.Controller):
)
pager_begin = (page - 1) * self._post_comment_per_page
pager_end = page * self._post_comment_per_page
- blog_post.website_message_ids = blog_post.website_message_ids[pager_begin:pager_end]
+ comments = blog_post.website_message_ids[pager_begin:pager_end]
tag = None
if tag_id:
@@ -226,6 +226,7 @@ class WebsiteBlog(http.Controller):
'post_url': post_url,
'blog_url': blog_url,
'pager': pager,
+ 'comments': comments,
}
response = request.website.render("website_blog.blog_post_complete", values)
response.set_cookie('visited_blogs', ','.join(map(str, visited_ids)))
diff --git a/addons/website_blog/models/website_blog.py b/addons/website_blog/models/website_blog.py
index 2862ca84e1c..9953b5f1ac8 100644
--- a/addons/website_blog/models/website_blog.py
+++ b/addons/website_blog/models/website_blog.py
@@ -61,7 +61,7 @@ class BlogPost(osv.Model):
'content': fields.html('Content', translate=True),
# website control
'website_published': fields.boolean(
- 'Publish', help="Publish on the website"
+ 'Publish', help="Publish on the website", copy=False,
),
'website_message_ids': fields.one2many(
'mail.message', 'res_id',
@@ -184,17 +184,6 @@ class BlogPost(osv.Model):
self.create_history(cr, uid, ids, vals, context)
return result
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default.update({
- 'website_message_ids': [],
- 'website_published': False,
- 'website_published_datetime': False,
- })
- return super(BlogPost, self).copy(cr, uid, id, default=default, context=context)
-
-
class BlogPostHistory(osv.Model):
_name = "blog.post.history"
_description = "Blog Post History"
diff --git a/addons/website_blog/views/website_blog_templates.xml b/addons/website_blog/views/website_blog_templates.xml
index c4bcb2d21fc..cdee16da86d 100644
--- a/addons/website_blog/views/website_blog_templates.xml
+++ b/addons/website_blog/views/website_blog_templates.xml
@@ -222,7 +222,7 @@
-
+
diff --git a/addons/website_blog/wizard/document_page_create_menu.py b/addons/website_blog/wizard/document_page_create_menu.py
deleted file mode 100644
index 9f20fab0d99..00000000000
--- a/addons/website_blog/wizard/document_page_create_menu.py
+++ /dev/null
@@ -1,86 +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 .
-#
-##############################################################################
-
-from openerp.osv import fields, osv
-
-class document_page_create_menu(osv.osv_memory):
- """ Create Menu """
- _name = "document.page.create.menu"
- _description = "Wizard Create Menu"
-
- _columns = {
- 'menu_name': fields.char('Menu Name', size=256, required=True),
- 'menu_parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', required=True),
- }
-
- def default_get(self, cr, uid, fields, context=None):
- if context is None:
- context = {}
- res = super(document_page_create_menu,self).default_get(cr, uid, fields, context=context)
- page_id = context.get('active_id')
- obj_page = self.pool.get('document.page')
- page = obj_page.browse(cr, uid, page_id, context=context)
- res['menu_name'] = page.name
- return res
-
- def document_page_menu_create(self, cr, uid, ids, context=None):
- if context is None:
- context = {}
- obj_page = self.pool.get('document.page')
- obj_view = self.pool.get('ir.ui.view')
- obj_menu = self.pool.get('ir.ui.menu')
- obj_action = self.pool.get('ir.actions.act_window')
- page_id = context.get('active_id', False)
- page = obj_page.browse(cr, uid, page_id, context=context)
-
- datas = self.browse(cr, uid, ids, context=context)
- data = False
- if datas:
- data = datas[0]
- if not data:
- return {}
- value = {
- 'name': 'Document Page',
- 'view_type': 'form',
- 'view_mode': 'form,tree',
- 'res_model': 'document.page',
- 'view_id': False,
- 'type': 'ir.actions.act_window',
- 'target': 'inlineview',
- }
- value['domain'] = "[('parent_id','=',%d)]" % (page.id)
- value['res_id'] = page.id
-
- action_id = obj_action.create(cr, uid, value)
- menu_id = obj_menu.create(cr, uid, {
- 'name': data.menu_name,
- 'parent_id':data.menu_parent_id.id,
- 'icon': 'STOCK_DIALOG_QUESTION',
- 'action': 'ir.actions.act_window,'+ str(action_id),
- }, context)
- obj_page.write(cr, uid, [page_id], {'menu_id':menu_id})
- return {
- 'type': 'ir.actions.client',
- 'tag': 'reload',
- }
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/website_blog/wizard/document_page_create_menu_view.xml b/addons/website_blog/wizard/document_page_create_menu_view.xml
deleted file mode 100644
index d0106faf6e8..00000000000
--- a/addons/website_blog/wizard/document_page_create_menu_view.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
- Create Menu
- document.page.create.menu
-
-
-
-
-
-
-
-
-
-
-
-
- Create Menu
- ir.actions.act_window
- document.page.create.menu
- form
- form
- new
-
-
-
-
diff --git a/addons/website_crm/controllers/main.py b/addons/website_crm/controllers/main.py
index 37d661e8a04..9b47140b9e3 100644
--- a/addons/website_crm/controllers/main.py
+++ b/addons/website_crm/controllers/main.py
@@ -1,13 +1,12 @@
# -*- coding: utf-8 -*-
import base64
-from openerp.tools.translate import _
-from openerp.addons.web import http
-from openerp.addons.web.http import request
-from openerp import SUPERUSER_ID
-
+import werkzeug
import werkzeug.urls
+from openerp import http, SUPERUSER_ID
+from openerp.http import request
+from openerp.tools.translate import _
class contactus(http.Controller):
diff --git a/addons/website_crm_partner_assign/controllers/main.py b/addons/website_crm_partner_assign/controllers/main.py
index ca08478d8d1..c104b26ce27 100644
--- a/addons/website_crm_partner_assign/controllers/main.py
+++ b/addons/website_crm_partner_assign/controllers/main.py
@@ -109,7 +109,7 @@ class WebsiteCrmPartnerAssign(http.Controller):
context=request.context) # todo in trunk: order="grade_id DESC, implemented_count DESC", offset=pager['offset'], limit=self._references_per_page
partners = partner_obj.browse(request.cr, SUPERUSER_ID, partner_ids, request.context)
# remove me in trunk
- partners.sort(key=lambda x: (-1 * (x.grade_id and x.grade_id.id or 0), len(x.implemented_partner_ids)), reverse=True)
+ partners = sorted(partners, key=lambda x: (-1 * (x.grade_id and x.grade_id.id or 0), len(x.implemented_partner_ids)), reverse=True)
partners = partners[pager['offset']:pager['offset'] + self._references_per_page]
google_map_partner_ids = ','.join(map(str, [p.id for p in partners]))
diff --git a/addons/website_crm_partner_assign/models/res_partner.py b/addons/website_crm_partner_assign/models/res_partner.py
index cc00f8bd6e1..44e45e235fb 100644
--- a/addons/website_crm_partner_assign/models/res_partner.py
+++ b/addons/website_crm_partner_assign/models/res_partner.py
@@ -4,5 +4,5 @@ from openerp.osv import osv, fields
class res_partner_grade(osv.osv):
_inherit = 'res.partner.grade'
_columns = {
- 'website_published': fields.boolean('Published On Website'),
+ 'website_published': fields.boolean('Published On Website', copy=False),
}
diff --git a/addons/website_event/models/event.py b/addons/website_event/models/event.py
index 33eee7c7829..c13ba88a17f 100644
--- a/addons/website_event/models/event.py
+++ b/addons/website_event/models/event.py
@@ -89,7 +89,7 @@ class event(osv.osv):
_columns = {
'twitter_hashtag': fields.char('Twitter Hashtag'),
- 'website_published': fields.boolean('Visible in Website'),
+ 'website_published': fields.boolean('Visible in Website', copy=False),
# TDE TODO FIXME: when website_mail/mail_thread.py inheritance work -> this field won't be necessary
'website_message_ids': fields.one2many(
'mail.message', 'res_id',
diff --git a/addons/website_event_sale/models/sale_order.py b/addons/website_event_sale/models/sale_order.py
index 4c1a83681ea..7351279eee0 100644
--- a/addons/website_event_sale/models/sale_order.py
+++ b/addons/website_event_sale/models/sale_order.py
@@ -30,7 +30,7 @@ class sale_order(osv.Model):
else:
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
if product.event_ticket_ids:
- event_ticket_id = product.event_ticket_ids[0]
+ event_ticket_id = product.event_ticket_ids[0].id
if event_ticket_id:
ticket = self.pool.get('event.event.ticket').browse(cr, uid, event_ticket_id, context=context)
diff --git a/addons/website_event_track/models/event.py b/addons/website_event_track/models/event.py
index 954598f0bd2..657e73d946f 100644
--- a/addons/website_event_track/models/event.py
+++ b/addons/website_event_track/models/event.py
@@ -86,7 +86,7 @@ class event_track(osv.osv):
'event_id': fields.many2one('event.event', 'Event', required=True),
'color': fields.integer('Color Index'),
'priority': fields.selection([('3','Low'),('2','Medium (*)'),('1','High (**)'),('0','Highest (***)')], 'Priority', required=True),
- 'website_published': fields.boolean('Available in the website'),
+ 'website_published': fields.boolean('Available in the website', copy=False),
'website_url': fields.function(_website_url, string="Website url", type="char"),
'image': fields.related('speaker_ids', 'image', type='binary', readonly=True)
}
@@ -120,7 +120,8 @@ class event_track(osv.osv):
#
class event_event(osv.osv):
_inherit = "event.event"
- def _tz_get(self,cr,uid, context=None):
+
+ def _list_tz(self,cr,uid, context=None):
# put POSIX 'Etc/*' entries at the end to avoid confusing users - see bug 1086728
return [(tz,tz) for tz in sorted(pytz.all_timezones, key=lambda tz: tz if not tz.startswith('Etc/') else '_')]
@@ -140,8 +141,8 @@ class event_event(osv.osv):
_columns = {
'tag_ids': fields.many2many('event.tag', string='Tags'),
- 'track_ids': fields.one2many('event.track', 'event_id', 'Tracks'),
- 'sponsor_ids': fields.one2many('event.sponsor', 'event_id', 'Sponsorships'),
+ 'track_ids': fields.one2many('event.track', 'event_id', 'Tracks', copy=True),
+ 'sponsor_ids': fields.one2many('event.sponsor', 'event_id', 'Sponsorships', copy=True),
'blog_id': fields.many2one('blog.blog', 'Event Blog'),
'show_track_proposal': fields.boolean('Talks Proposals'),
'show_tracks': fields.boolean('Multiple Tracks'),
@@ -149,7 +150,7 @@ class event_event(osv.osv):
'count_tracks': fields.function(_count_tracks, type='integer', string='Tracks'),
'tracks_tag_ids': fields.function(_get_tracks_tag_ids, type='one2many', relation='event.track.tag', string='Tags of Tracks'),
'allowed_track_tag_ids': fields.many2many('event.track.tag', string='Accepted Tags', help="List of available tags for track proposals."),
- 'timezone_of_event': fields.selection(_tz_get, 'Event Timezone', size=64),
+ 'timezone_of_event': fields.selection(_list_tz, 'Event Timezone', size=64),
}
_defaults = {
diff --git a/addons/website_forum_doc/controllers/main.py b/addons/website_forum_doc/controllers/main.py
index 2b951ee978d..71563d78f01 100644
--- a/addons/website_forum_doc/controllers/main.py
+++ b/addons/website_forum_doc/controllers/main.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
-from openerp.addons.web import http
-from openerp.addons.web.http import request
+from openerp import http
+from openerp.http import request
from openerp.addons.website.models.website import slug
diff --git a/addons/website_gengo/controllers/main.py b/addons/website_gengo/controllers/main.py
index f80c7cbb34b..f373c9375a3 100644
--- a/addons/website_gengo/controllers/main.py
+++ b/addons/website_gengo/controllers/main.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
import openerp
-from openerp.addons.web import http
-from openerp.addons.web.http import request
+from openerp import http
+from openerp.http import request
import time
GENGO_DEFAULT_LIMIT = 20
diff --git a/addons/website_hr/controllers/main.py b/addons/website_hr/controllers/main.py
index fcc66e6bae8..430c47b5dfa 100644
--- a/addons/website_hr/controllers/main.py
+++ b/addons/website_hr/controllers/main.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from openerp.addons.web import http
-from openerp.addons.web.http import request
+from openerp import http
+from openerp.http import request
class website_hr(http.Controller):
diff --git a/addons/website_hr/models/hr.py b/addons/website_hr/models/hr.py
index 4cacc56dba4..2927993a6d4 100644
--- a/addons/website_hr/models/hr.py
+++ b/addons/website_hr/models/hr.py
@@ -6,7 +6,7 @@ from openerp.osv import osv, fields
class hr(osv.osv):
_inherit = 'hr.employee'
_columns = {
- 'website_published': fields.boolean('Available in the website'),
+ 'website_published': fields.boolean('Available in the website', copy=False),
'public_info': fields.text('Public Info'),
}
_defaults = {
diff --git a/addons/website_hr_recruitment/controllers/main.py b/addons/website_hr_recruitment/controllers/main.py
index 84a0ec93454..c99c6644f9c 100644
--- a/addons/website_hr_recruitment/controllers/main.py
+++ b/addons/website_hr_recruitment/controllers/main.py
@@ -2,9 +2,9 @@
import base64
from openerp import SUPERUSER_ID
-from openerp.addons.web import http
+from openerp import http
from openerp.tools.translate import _
-from openerp.addons.web.http import request
+from openerp.http import request
class website_hr_recruitment(http.Controller):
@http.route([
diff --git a/addons/website_hr_recruitment/models/hr_job.py b/addons/website_hr_recruitment/models/hr_job.py
index 26aad8ae1b8..b14361db333 100644
--- a/addons/website_hr_recruitment/models/hr_job.py
+++ b/addons/website_hr_recruitment/models/hr_job.py
@@ -17,7 +17,7 @@ class hr_job(osv.osv):
return super(hr_job, self).job_open(cr, uid, ids, context)
_columns = {
- 'website_published': fields.boolean('Published'),
+ 'website_published': fields.boolean('Published', copy=False),
'website_description': fields.html('Website description'),
'website_url': fields.function(_website_url, string="Website URL", type="char"),
}
diff --git a/addons/website_mail/controllers/email_designer.py b/addons/website_mail/controllers/email_designer.py
index a9462f7be7d..0a3e1043fad 100644
--- a/addons/website_mail/controllers/email_designer.py
+++ b/addons/website_mail/controllers/email_designer.py
@@ -17,6 +17,7 @@ class WebsiteEmailDesigner(http.Controller):
'email' not in model_cols and 'email_from' not in model_cols or \
'name' not in model_cols and 'subject' not in model_cols:
return request.redirect('/')
+ res_id = int(res_id)
obj_ids = request.registry[model].exists(request.cr, request.uid, [res_id], context=request.context)
if not obj_ids:
return request.redirect('/')
@@ -33,7 +34,6 @@ class WebsiteEmailDesigner(http.Controller):
body_field = 'body_html'
cr, uid, context = request.cr, request.uid, request.context
- res_id = int(res_id)
record = request.registry[model].browse(cr, uid, res_id, context=context)
values = {
diff --git a/addons/website_mail/models/mail_message.py b/addons/website_mail/models/mail_message.py
index cff950f2962..4206872199c 100644
--- a/addons/website_mail/models/mail_message.py
+++ b/addons/website_mail/models/mail_message.py
@@ -43,7 +43,7 @@ class MailMessage(osv.Model):
help='Message description: either the subject, or the beginning of the body'
),
'website_published': fields.boolean(
- 'Published', help="Visible on the website as a comment"
+ 'Published', help="Visible on the website as a comment", copy=False,
),
}
diff --git a/addons/website_mail/models/mail_thread.py b/addons/website_mail/models/mail_thread.py
index bea9178a335..cb19b52cf66 100644
--- a/addons/website_mail/models/mail_thread.py
+++ b/addons/website_mail/models/mail_thread.py
@@ -36,8 +36,3 @@ class MailThread(osv.AbstractModel):
),
}
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default['website_message_ids'] = []
- return super(MailThread, self).copy(cr, uid, id, default=default, context=context)
\ No newline at end of file
diff --git a/addons/website_membership/controllers/main.py b/addons/website_membership/controllers/main.py
index 52ba31324f0..9947eab4d98 100644
--- a/addons/website_membership/controllers/main.py
+++ b/addons/website_membership/controllers/main.py
@@ -73,7 +73,7 @@ class WebsiteMembership(http.Controller):
# displayed membership lines
membership_line_ids = membership_line_obj.search(cr, uid, line_domain, context=context)
membership_lines = membership_line_obj.browse(cr, uid, membership_line_ids, context=context)
- membership_lines.sort(key=lambda x: x.membership_id.website_sequence)
+ membership_lines = sorted(membership_lines, key=lambda x: x.membership_id.website_sequence)
partner_ids = [m.partner.id for m in membership_lines]
google_map_partner_ids = ",".join(map(str, partner_ids))
diff --git a/addons/website_membership/models/product.py b/addons/website_membership/models/product.py
index c45a7a568a5..1de254de78b 100644
--- a/addons/website_membership/models/product.py
+++ b/addons/website_membership/models/product.py
@@ -25,7 +25,7 @@ class product_template(osv.Model):
_inherit = 'product.template'
_columns = {
- 'website_published': fields.boolean('Available in the website'),
+ 'website_published': fields.boolean('Available in the website', copy=False),
}
_defaults = {
'website_published': False,
diff --git a/addons/website_partner/models/res_partner.py b/addons/website_partner/models/res_partner.py
index e762455debe..08fc29ce75f 100644
--- a/addons/website_partner/models/res_partner.py
+++ b/addons/website_partner/models/res_partner.py
@@ -12,7 +12,7 @@ class WebsiteResPartner(osv.Model):
_columns = {
'website_published': fields.boolean(
- 'Publish', help="Publish on the website"),
+ 'Publish', help="Publish on the website", copy=False),
'website_description': fields.html(
'Website Partner Full Description'
),
diff --git a/addons/website_quote/models/order.py b/addons/website_quote/models/order.py
index aaac80ceb5f..c7ae4bc8ab5 100644
--- a/addons/website_quote/models/order.py
+++ b/addons/website_quote/models/order.py
@@ -32,9 +32,9 @@ class sale_quote_template(osv.osv):
_columns = {
'name': fields.char('Quotation Template', required=True),
'website_description': fields.html('Description', translate=True),
- 'quote_line': fields.one2many('sale.quote.line', 'quote_id', 'Quote Template Lines'),
+ 'quote_line': fields.one2many('sale.quote.line', 'quote_id', 'Quote Template Lines', copy=True),
'note': fields.text('Terms and conditions'),
- 'options': fields.one2many('sale.quote.option', 'template_id', 'Optional Products Lines'),
+ 'options': fields.one2many('sale.quote.option', 'template_id', 'Optional Products Lines', copy=True),
'number_of_days': fields.integer('Quote Duration', help='Number of days for the validaty date computation of the quotation'),
}
def open_template(self, cr, uid, quote_id, context=None):
@@ -110,7 +110,7 @@ class sale_order(osv.osv):
return res
_columns = {
- 'access_token': fields.char('Security Token', required=True),
+ 'access_token': fields.char('Security Token', required=True, copy=False),
'template_id': fields.many2one('sale.quote.template', 'Quote Template'),
'website_description': fields.html('Description'),
'options' : fields.one2many('sale.order.option', 'order_id', 'Optional Products Lines'),
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index e56e3bb1478..2c52e21e281 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -2,8 +2,8 @@
import werkzeug
from openerp import SUPERUSER_ID
-from openerp.addons.web import http
-from openerp.addons.web.http import request
+from openerp import http
+from openerp.http import request
from openerp.tools.translate import _
from openerp.addons.website.models.website import slug
@@ -582,7 +582,7 @@ class website_sale(http.Controller):
# acquirer_ids = [tx.acquirer_id.id]
# else:
acquirer_ids = payment_obj.search(cr, SUPERUSER_ID, [('website_published', '=', True)], context=context)
- values['acquirers'] = payment_obj.browse(cr, uid, acquirer_ids, context=context)
+ values['acquirers'] = list(payment_obj.browse(cr, uid, acquirer_ids, context=context))
render_ctx = dict(context, submit_class='btn btn-primary', submit_txt='Pay Now')
for acquirer in values['acquirers']:
acquirer.button = payment_obj.render(
diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py
index ce206372047..29e1fca7f57 100644
--- a/addons/website_sale/models/product.py
+++ b/addons/website_sale/models/product.py
@@ -122,7 +122,7 @@ class product_template(osv.Model):
],
string='Website Comments',
),
- 'website_published': fields.boolean('Available in the website'),
+ 'website_published': fields.boolean('Available in the website', copy=False),
'website_description': fields.html('Description for the website'),
'alternative_product_ids': fields.many2many('product.template','product_alternative_rel','src_id','dest_id', string='Alternative Products', help='Appear on the product page'),
'accessory_product_ids': fields.many2many('product.product','product_accessory_rel','src_id','dest_id', string='Accessory Products', help='Appear on the shopping cart'),
diff --git a/addons/website_sale_delivery/models/sale_order.py b/addons/website_sale_delivery/models/sale_order.py
index 029190d35c8..ebea78803fd 100644
--- a/addons/website_sale_delivery/models/sale_order.py
+++ b/addons/website_sale_delivery/models/sale_order.py
@@ -8,7 +8,7 @@ from openerp.addons import decimal_precision
class delivery_carrier(orm.Model):
_inherit = 'delivery.carrier'
_columns = {
- 'website_published': fields.boolean('Available in the website'),
+ 'website_published': fields.boolean('Available in the website', copy=False),
'website_description': fields.text('Description for the website'),
}
_defaults = {
diff --git a/doc/03_module_dev_02.rst b/doc/03_module_dev_02.rst
index 223c82d3f1c..ec7fa57d0dc 100644
--- a/doc/03_module_dev_02.rst
+++ b/doc/03_module_dev_02.rst
@@ -615,6 +615,7 @@ Relational Types
reference. :guilabel:`relation` is the table to look up that
reference in.
+.. _fields-functional:
Functional Fields
+++++++++++++++++
diff --git a/doc/03_module_dev_03.rst b/doc/03_module_dev_03.rst
index e5e309fe00e..74fb4772f84 100644
--- a/doc/03_module_dev_03.rst
+++ b/doc/03_module_dev_03.rst
@@ -70,15 +70,21 @@ As we can see below in the purple zone of the screen, there is also a way to dis
On Change
+++++++++
-The on_change attribute defines a method that is called when the content of a view field has changed.
+The on_change attribute defines a method that is called when the
+content of a view field has changed.
-This method takes at least arguments: cr, uid, ids, which are the three classical arguments and also the context dictionary. You can add parameters to the method. They must correspond to other fields defined in the view, and must also be defined in the XML with fields defined this way::
+This method takes at least arguments: cr, uid, ids, which are the
+three classical arguments and also the context dictionary. You can add
+parameters to the method. They must correspond to other fields defined
+in the view, and must also be defined in the XML with fields defined
+this way::
The example below is from the sale order view.
-You can use the 'context' keyword to access data in the context that can be used as params of the function.::
+You can use the 'context' keyword to access data in the context that
+can be used as params of the function.::
@@ -100,7 +106,10 @@ You can use the 'context' keyword to access data in the context that can be used
return {'value':v}
-When editing the shop_id form field, the onchange_shop_id method of the sale_order object is called and returns a dictionary where the 'value' key contains a dictionary of the new value to use in the 'project_id', 'pricelist_id' and 'payment_default_id' fields.
+When editing the shop_id form field, the onchange_shop_id method of
+the sale_order object is called and returns a dictionary where the
+'value' key contains a dictionary of the new value to use in the
+'project_id', 'pricelist_id' and 'payment_default_id' fields.
Note that it is possible to change more than just the values of
fields. For example, it is possible to change the value of some fields
diff --git a/doc/api_models.rst b/doc/api_models.rst
index 143147c0f48..82f45ed5c9c 100644
--- a/doc/api_models.rst
+++ b/doc/api_models.rst
@@ -1,7 +1,21 @@
-ORM and models
---------------
+ORM and Models
+==============
.. automodule:: openerp.osv.orm
:members:
:undoc-members:
+
+Scope Management
+================
+
+.. automodule:: openerp.osv.scope
+ :members:
+ :undoc-members:
+
+API Decorators
+==============
+
+.. automodule:: openerp.osv.api
+ :members:
+ :undoc-members:
diff --git a/doc/howto/howto_website.rst b/doc/howto/howto_website.rst
index e4ef9b4748b..722891519da 100644
--- a/doc/howto/howto_website.rst
+++ b/doc/howto/howto_website.rst
@@ -65,7 +65,7 @@ module in it:
.. code-block:: console
$ createdb academy
- $ ./openerp-server --addons-path=../web/addons,../addons,../my-modules \
+ $ ./openerp-server --addons-path=addons,../my-modules \
-d academy -i academy --db-filter=academy
* ``--addons-path`` tells OpenERP where it can find its modules. By default it
@@ -159,6 +159,10 @@ Let's move our 2 pseudo-templates from inline strings to actual templates:
This simplifies the controller code by moving data formatting out of it, and
generally makes it simpler for designers to edit the markup.
+.. note::
+
+ You'll need to update the module to install the new templates
+
.. todo:: link to section about reusing/altering existing stuff, template
overriding
@@ -175,7 +179,7 @@ First, we'll install the ``website`` module: restart your server with
.. code-block:: console
- $ ./openerp-server --addons-path=../web/addons,../addons,../my-modules \
+ $ ./openerp-server --addons-path=addons,../my-modules \
-d academy -i website --db-filter=academy
If you navigate to `your openerp`_, your basic page may have been replaced by
@@ -202,7 +206,7 @@ ensures ``academy``'s index page overwrites ``website``'s.
.. code-block:: console
- $ ./openerp-server --addons-path=../web/addons,../addons,../my-modules \
+ $ ./openerp-server --addons-path=addons,../my-modules \
-d academy -u academy --db-filter=academy
instead of the previous command (note: ``-i`` was replaced by ``-u``)
@@ -460,14 +464,14 @@ of all records in the object) and the "form" view (view an manipulation of a
single record). The :guilabel:`Create` button above the list lets you create
new record, you can select records to delete them.
-There's one big issue to fix right now, the labeling of the column in the list
-and the fields in the form view, which are all currently :guilabel:`unknown`.
-We can fix that by adding a ``string`` attribute to the model field:
+The names of the fields in the search and list view are automatically inferred
+from the logical field names, but it's probably a good idea to specify them
+anyway, by adding a ``string`` to the model field:
.. patch::
-The second problem is that the list view only displays the ``name`` field. To
-fix this, we have to create an explicit list view for lectures:
+An issue is that the list view only displays the ``name`` field. To fix this,
+we have to create an explicit list view for lectures:
.. patch::
@@ -491,7 +495,7 @@ the server as:
.. code-block:: console
- $ ./openerp-server --addons-path=../web/addons,../addons,../my-modules \
+ $ ./openerp-server --addons-path=addons,../my-modules \
-d academy -i website_event --db-filter=academy
We'll also add it as a dependency to our module:
@@ -517,7 +521,7 @@ Restart the server with
.. code-block:: console
- $ ./openerp-server --addons-path=../web/addons,../addons,../my-modules \
+ $ ./openerp-server --addons-path=addons,../my-modules \
-d academy -i academy --db-filter=academy
and the menu item has been renamed to Lectures.
@@ -573,6 +577,12 @@ The gist of the operation is fairly simple, but there are lots of changes:
purged if we do not need it (e.g. existing non-lectures events and event
types can be removed before adding our own)
+.. note::
+
+ because we're reusing the old XIDs on completely different models, we need
+ to either remove the old reference or (simpler) just drop and re-create
+ the database
+
.. patch::
Our data is back in the fontend (site), and in the backend we get
diff --git a/doc/howto/howto_website/basic-page b/doc/howto/howto_website/basic-page
index d3179145542..ccb47652f39 100644
--- a/doc/howto/howto_website/basic-page
+++ b/doc/howto/howto_website/basic-page
@@ -1,5 +1,5 @@
# HG changeset patch
-# Parent b96cd22d25cfa9a67f451d091f5c4896997d350d
+# Parent 11a30af5c0c80b15a2bbad562ef3a580ee99fb3b
diff --git a/controllers/academy.py b/controllers/academy.py
--- a/controllers/academy.py
@@ -10,7 +10,7 @@ diff --git a/controllers/academy.py b/controllers/academy.py
def index(self):
- return "Hello, world!"
+ return """
-+
++
+
+ Hello, world!
+
diff --git a/doc/howto/howto_website/field-label b/doc/howto/howto_website/field-label
index 2589e8f872a..090344dc719 100644
--- a/doc/howto/howto_website/field-label
+++ b/doc/howto/howto_website/field-label
@@ -1,14 +1,14 @@
# HG changeset patch
-# Parent fe4edbcd9e98db81ec6321c58e8ac508a686f45b
-diff -r fe4edbcd9e98 -r 72a099819e5b models/academy.py
---- a/models/academy.py Mon Apr 14 16:38:10 2014 +0200
-+++ b/models/academy.py Mon Apr 14 16:59:01 2014 +0200
-@@ -14,6 +14,6 @@ class Lectures(orm.Model):
+# Parent 4b38aba926d27af5f81be1a3b5a482c26522bf38
+
+diff --git a/models/academy.py b/models/academy.py
+--- a/models/academy.py
++++ b/models/academy.py
+@@ -12,5 +12,5 @@ class Lectures(Model):
+ _name = 'academy.lectures'
_order = 'date ASC'
- _columns = {
-- 'name': fields.char(required=True),
-- 'date': fields.date(required=True),
-+ 'name': fields.char(required=True, string="Name"),
-+ 'date': fields.date(required=True, string="Date"),
- }
+- name = Char(required=True)
+- date = Date(required=True)
++ name = Char(required=True, string="Name")
++ date = Date(required=True, string="Date")
diff --git a/doc/howto/howto_website/lectures-model-add b/doc/howto/howto_website/lectures-model-add
index e622ec6656f..3211995ab72 100644
--- a/doc/howto/howto_website/lectures-model-add
+++ b/doc/howto/howto_website/lectures-model-add
@@ -1,5 +1,5 @@
# HG changeset patch
-# Parent cd98e5752eedca2780da80387ac01c8cd166940f
+# Parent 13c65b2eed8f84b951ff5a98051f64fe35ddcf0a
diff --git a/__openerp__.py b/__openerp__.py
--- a/__openerp__.py
@@ -15,22 +15,17 @@ diff --git a/__openerp__.py b/__openerp__.py
diff --git a/controllers/academy.py b/controllers/academy.py
--- a/controllers/academy.py
+++ b/controllers/academy.py
-@@ -6,10 +6,15 @@ from openerp.addons.web.controllers impo
+@@ -6,8 +6,10 @@ from openerp.addons.web.controllers impo
class academy(main.Home):
@http.route('/', auth='public', website=True)
def index(self):
-+ cr, uid, context = http.request.cr, http.request.uid, http.request.context
-+ Lectures = http.request.registry['academy.lectures']
- tas = http.request.registry['academy.tas'].search_read(
- http.request.cr, http.request.uid, context=http.request.context)
-+ lectures = Lectures.browse(
-+ cr, uid, Lectures.search(cr, uid, [], context=context), context=context)
- return http.request.website.render('academy.index', {
- 'tas': tas,
++ lectures = http.request.env['academy.lectures'].search([])
+ tas = http.request.env['academy.tas'].search([])
+ return http.request.render('academy.index', {
+ 'lectures': lectures,
+ 'tas': tas,
})
- @http.route('/tas//', auth='public', website=True)
diff --git a/data/lectures.xml b/data/lectures.xml
new file mode 100644
--- /dev/null
@@ -63,19 +58,24 @@ new file mode 100644
diff --git a/models/academy.py b/models/academy.py
--- a/models/academy.py
+++ b/models/academy.py
-@@ -8,3 +8,12 @@ class TeachingAssistants(orm.Model):
- 'name': fields.char(),
- 'biography': fields.html(),
- }
+@@ -1,9 +1,16 @@
+ # -*- coding: utf-8 -*-
+ from openerp.models import Model
+-from openerp.fields import Char, Html
++from openerp.fields import Char, Html, Date
+
+ class TeachingAssistants(Model):
+ _name = "academy.tas"
+
+ name = Char()
+ biography = Html()
+
-+class Lectures(orm.Model):
++class Lectures(Model):
+ _name = 'academy.lectures'
+ _order = 'date ASC'
+
-+ _columns = {
-+ 'name': fields.char(required=True),
-+ 'date': fields.date(required=True),
-+ }
++ name = Char(required=True)
++ date = Date(required=True)
diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
--- a/security/ir.model.access.csv
+++ b/security/ir.model.access.csv
diff --git a/doc/howto/howto_website/manifest b/doc/howto/howto_website/manifest
index 601c18c5106..c0c7ee29547 100644
--- a/doc/howto/howto_website/manifest
+++ b/doc/howto/howto_website/manifest
@@ -71,16 +71,15 @@ diff --git a/models/academy.py b/models/academy.py
new file mode 100644
--- /dev/null
+++ b/models/academy.py
-@@ -0,0 +1,9 @@
+@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
-+from openerp.osv import orm, fields
++from openerp.models import Model
++from openerp.fields import Char
+
-+class academy(orm.Model):
++class academy(Model):
+ _name = "academy.academy"
+
-+ _columns = {
-+ 'name': fields.char(),
-+ }
++ name = Char()
diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
new file mode 100644
--- /dev/null
diff --git a/doc/howto/howto_website/move-to-openerp-objects b/doc/howto/howto_website/move-to-openerp-objects
index 8623d8a90e5..0efebb39846 100644
--- a/doc/howto/howto_website/move-to-openerp-objects
+++ b/doc/howto/howto_website/move-to-openerp-objects
@@ -1,5 +1,5 @@
# HG changeset patch
-# Parent ade038cd6dfd855f1d423cffb3d4e242404c28f0
+# Parent bc8914ae6b552520bdc3856e81d3624dc5a77a99
diff --git a/__init__.py b/__init__.py
--- a/__init__.py
@@ -26,40 +26,31 @@ diff --git a/__openerp__.py b/__openerp__.py
diff --git a/controllers/academy.py b/controllers/academy.py
--- a/controllers/academy.py
+++ b/controllers/academy.py
-@@ -6,18 +6,29 @@ from openerp.addons.web.controllers impo
+@@ -6,14 +6,22 @@ from openerp.addons.web.controllers impo
class academy(main.Home):
@http.route('/', auth='public', website=True)
def index(self):
-+ registry = http.request.registry
- cr, uid, context = http.request.cr, http.request.uid, http.request.context
-- Lectures = http.request.registry['academy.lectures']
-- tas = http.request.registry['academy.tas'].search_read(
-- http.request.cr, http.request.uid, context=http.request.context)
+- lectures = http.request.env['academy.lectures'].search([])
+- tas = http.request.env['academy.tas'].search([])
++ ta_group = http.request.env.ref('academy.tas')
++ tas = http.request.env['res.users'].search(
++ [('groups_id', '=', [ta_group.id])]
++ )
+
-+ Data = registry['ir.model.data']
-+ _, ta_group_id = Data.get_object_reference(cr, uid, 'academy', 'tas')
-+ tas = registry['res.users'].search_read(
-+ http.request.cr, http.request.uid,
-+ [('groups_id', '=', [ta_group_id])],
-+ context=http.request.context)
++ lecture_type = http.request.env.ref('academy.lecture_type')
++ lectures = http.request.env['event.event'].search(
++ [('type', '=', lecture_type.id)]
++ )
+
-+ Lectures = registry['event.event']
-+ _, lecture_type_id = Data.get_object_reference(cr, uid, 'academy', 'lecture_type')
- lectures = Lectures.browse(
-- cr, uid, Lectures.search(cr, uid, [], context=context), context=context)
-+ cr, uid,
-+ Lectures.search(cr, uid, [('type', '=', lecture_type_id),], context=context),
-+ context=context)
-+
- return http.request.website.render('academy.index', {
- 'tas': tas,
+ return http.request.render('academy.index', {
'lectures': lectures,
+ 'tas': tas,
})
- @http.route('/tas//', auth='public', website=True)
+ @http.route('/tas//', auth='public', website=True)
def ta(self, ta):
- return http.request.website.render('academy.ta', {
+ return http.request.render('academy.ta', {
'ta': ta,
diff --git a/data/views.xml b/data/views.xml
deleted file mode 100644
@@ -227,39 +218,36 @@ diff --git a/models/academy.py b/models/academy.py
deleted file mode 100644
--- a/models/academy.py
+++ /dev/null
-@@ -1,19 +0,0 @@
+@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
--from openerp.osv import orm, fields
+-from openerp.models import Model
+-from openerp.fields import Char, Html, Date
-
--class TeachingAssistants(orm.Model):
+-class TeachingAssistants(Model):
- _name = "academy.tas"
-
-- _columns = {
-- 'name': fields.char(),
-- 'biography': fields.html(),
-- }
+- name = Char()
+- biography = Html()
-
--class Lectures(orm.Model):
+-class Lectures(Model):
- _name = 'academy.lectures'
- _order = 'date ASC'
-
-- _columns = {
-- 'name': fields.char(required=True, string="Name"),
-- 'date': fields.date(required=True, string="Date"),
-- }
+- name = Char(required=True, string="Name")
+- date = Date(required=True, string="Date")
diff --git a/models/res_partner.py b/models/res_partner.py
new file mode 100644
--- /dev/null
+++ b/models/res_partner.py
@@ -0,0 +1,8 @@
-+from openerp.osv import orm, fields
++from openerp.models import Model
++from openerp.fields import Html
+
-+class Partner(orm.Model):
++class Partner(Model):
+ _inherit = 'res.partner'
+
-+ _columns = {
-+ 'biography': fields.html(),
-+ }
++ biography = Html()
++
diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
deleted file mode 100644
--- a/security/ir.model.access.csv
diff --git a/doc/howto/howto_website/ta-controller b/doc/howto/howto_website/ta-controller
index 64de1dae17b..da4ee7606c9 100644
--- a/doc/howto/howto_website/ta-controller
+++ b/doc/howto/howto_website/ta-controller
@@ -1,5 +1,5 @@
# HG changeset patch
-# Parent a110c540b0769ee849a404324cf8594d116cc982
+# Parent a813df5b9cbdf5db9b0c3f6bac47b1821ddbb086
diff --git a/controllers/academy.py b/controllers/academy.py
--- a/controllers/academy.py
@@ -25,7 +25,7 @@ diff --git a/controllers/academy.py b/controllers/academy.py
+ ]
+
return """
-
+
- Hello, world!
+
diff --git a/doc/index.rst b/doc/index.rst
index ad42e41a10f..f7cd7c399f1 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -36,9 +36,10 @@ OpenERP Server API
.. toctree::
:maxdepth: 1
- orm-methods.rst
- api_models.rst
- routing.rst
+ new_api
+ orm-methods
+ api_models
+ routing
Changelog
'''''''''
diff --git a/doc/new_api.rst b/doc/new_api.rst
new file mode 100644
index 00000000000..e99d5b7eef4
--- /dev/null
+++ b/doc/new_api.rst
@@ -0,0 +1,138 @@
+==================
+High-level ORM API
+==================
+
+.. _compute:
+
+Computed fields: defaults and function fields
+=============================================
+
+The high-level API attempts to unify concepts of programmatic value generation
+for function fields (stored or not) and default values through the use of
+computed fields.
+
+Fields are marked as computed by setting their ``compute`` attribute to the
+name of the method used to compute then::
+
+ has_sibling = fields.Integer(compute='compute_has_sibling')
+
+by default computation methods behave as simple defaults in case no
+corresponding value is found in the database::
+
+ def default_number_of_employees(self):
+ self.number_of_employees = 1
+
+.. todo::
+
+ literal defaults::
+
+ has_sibling = fields.Integer(compute=fields.default(1))
+
+but they can also be used for computed fields by specifying fields used for
+the computation. The dependencies can be dotted for "cascading" through
+related models::
+
+ @api.depends('parent_id.children_count')
+ def compute_has_sibling(self):
+ self.has_sibling = self.parent_id.children_count >= 2
+
+.. todo::
+
+ function-based::
+
+ has_sibling = fields.Integer()
+ @has_sibling.computer
+ @api.depends('parent_id.children_count')
+ def compute_has_sibling(self):
+ self.has_sibling = self.parent_id.children_count >= 2
+
+note that computation methods (defaults or others) do not *return* a value,
+they *set* values the current object. This means the high-level API does not
+need :ref:`an explicit multi `: a ``multi`` method is
+simply one which computes several values at once::
+
+ @api.depends('company_id')
+ def compute_relations(self):
+ self.computed_company = self.company_id
+ self.computed_companies = self.company_id.to_recordset()
+
+Automatic onchange
+==================
+
+Using to the improved and expanded :ref:`computed fields `, the
+high-level ORM API is able to infer the effect of fields on
+one another, and thus automatically provide a basic form of onchange without
+having to implement it by hand, or implement dozens of onchange functions to
+get everything right.
+
+
+
+
+.. todo::
+
+ deferred records::
+
+ partner = Partner.record(42, defer=True)
+ partner.name = "foo"
+ partner.user_id = juan
+ partner.save() # only saved to db here
+
+ with scope.defer():
+ # all records in this scope or children scopes are deferred
+ # until corresponding scope poped or until *this* scope poped?
+ partner = Partner.record(42)
+ partner.name = "foo"
+ partner.user_id = juan
+ # saved here, also for recordset &al, ~transaction
+
+ # temp deferment, maybe simpler? Or for bulk operations?:
+ with Partner.record(42) as partner:
+ partner.name = "foo"
+ partner.user_id = juan
+
+ ``id = False`` => always defered? null v draft?
+
+.. todo:: keyword arguments passed positionally (common for context, completely breaks everything)
+
+.. todo:: optional arguments (report_aged_receivable)
+
+.. todo:: non-id ids? (mail thread_id)
+
+.. todo:: partial signatures on overrides (e.g. message_post)
+
+.. todo::
+
+ ::
+
+ field = fields.Char()
+
+ @field.computer
+ def foo(self):
+ "compute foo here"
+
+ ~
+
+ ::
+
+ field = fields.Char(compute='foo')
+
+ def foo(self):
+ "compute foo here"
+
+.. todo:: doc
+
+.. todo:: incorrect dependency spec?
+
+.. todo:: dynamic dependencies?
+
+ ::
+
+ @api.depends(???)
+ def foo(self)
+ self.a = self[self.b]
+
+.. todo:: recursive onchange
+
+ Country & state. Change country -> remove state; set state -> set country
+
+.. todo:: onchange list affected?
diff --git a/openerp/__init__.py b/openerp/__init__.py
index 995822c9cb0..24a193bf759 100644
--- a/openerp/__init__.py
+++ b/openerp/__init__.py
@@ -67,9 +67,7 @@ def registry(database_name):
# Imports
#----------------------------------------------------------
import addons
-import cli
import conf
-import http
import loglevels
import modules
import netsvc
@@ -82,5 +80,18 @@ import sql_db
import tools
import workflow
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+#----------------------------------------------------------
+# Model classes, fields, api decorators, and translations
+#----------------------------------------------------------
+from . import models
+from . import fields
+from . import api
+from openerp.tools.translate import _
+#----------------------------------------------------------
+# Other imports, which may require stuff from above
+#----------------------------------------------------------
+import cli
+import http
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/__openerp__.py b/openerp/addons/base/__openerp__.py
index 6db84cb84c4..4bbe2ed5879 100644
--- a/openerp/addons/base/__openerp__.py
+++ b/openerp/addons/base/__openerp__.py
@@ -39,7 +39,6 @@ The kernel of OpenERP, needed for all installation.
'res/res_country_data.xml',
'security/base_security.xml',
'base_menu.xml',
- 'res/res_security.xml',
'res/res_config.xml',
'res/res.country.state.csv',
'ir/ir_actions.xml',
@@ -82,7 +81,7 @@ The kernel of OpenERP, needed for all installation.
'res/res_users_view.xml',
'res/res_partner_data.xml',
'res/ir_property_view.xml',
- 'security/base_security.xml',
+ 'res/res_security.xml',
'security/ir.model.access.csv',
],
'demo': [
diff --git a/openerp/addons/base/base_menu.xml b/openerp/addons/base/base_menu.xml
index d9cd0c4e8bf..5c15addb56d 100644
--- a/openerp/addons/base/base_menu.xml
+++ b/openerp/addons/base/base_menu.xml
@@ -30,6 +30,10 @@
+
+
+
+
Open Settings Menureload
diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py
index 2de2301c5df..7ac8783ddd9 100644
--- a/openerp/addons/base/ir/ir_actions.py
+++ b/openerp/addons/base/ir/ir_actions.py
@@ -330,7 +330,7 @@ class ir_actions_act_window(osv.osv):
}
for res in results:
model = res.get('res_model')
- if model and self.pool.get(model):
+ if model in self.pool:
try:
with tools.mute_logger("openerp.tools.safe_eval"):
eval_context = eval(res['context'] or "{}", eval_dict) or {}
@@ -339,7 +339,7 @@ class ir_actions_act_window(osv.osv):
continue
if not fields or 'help' in fields:
custom_context = dict(context, **eval_context)
- res['help'] = self.pool.get(model).get_empty_list_help(cr, uid, res.get('help', ""), context=custom_context)
+ res['help'] = self.pool[model].get_empty_list_help(cr, uid, res.get('help', ""), context=custom_context)
if ids_int:
return results[0]
return results
@@ -355,7 +355,7 @@ class ir_actions_act_window(osv.osv):
dataobj = self.pool.get('ir.model.data')
data_id = dataobj._get_id (cr, SUPERUSER_ID, module, xml_id)
res_id = dataobj.browse(cr, uid, data_id, context).res_id
- return self.read(cr, uid, res_id, [], context)
+ return self.read(cr, uid, [res_id], [], context)[0]
VIEW_TYPES = [
('tree', 'Tree'),
@@ -550,7 +550,7 @@ class ir_actions_server(osv.osv):
help="Provide an expression that, applied on the current record, gives the field to update."),
'fields_lines': fields.one2many('ir.server.object.lines', 'server_id',
string='Value Mapping',
- help=""),
+ copy=True),
# Fake fields used to implement the placeholder assistant
'model_object_field': fields.many2one('ir.model.fields', string="Field",
@@ -577,7 +577,7 @@ class ir_actions_server(osv.osv):
'sequence': 5,
'code': """# You can use the following variables:
# - self: ORM model of the record on which the action is triggered
-# - object: browse_record of the record on which the action is triggered if there is one, otherwise None
+# - object: Record on which the action is triggered if there is one, otherwise None
# - pool: ORM model pool (i.e. self.pool)
# - cr: database cursor
# - uid: current user id
@@ -838,7 +838,7 @@ class ir_actions_server(osv.osv):
def run_action_client_action(self, cr, uid, action, eval_context=None, context=None):
if not action.action_id:
raise osv.except_osv(_('Error'), _("Please specify an action to launch!"))
- return self.pool[action.action_id.type].read(cr, uid, action.action_id.id, context=context)
+ return self.pool[action.action_id.type].read(cr, uid, [action.action_id.id], context=context)[0]
def run_action_code_multi(self, cr, uid, action, eval_context=None, context=None):
eval(action.code.strip(), eval_context, mode="exec", nocopy=True) # nocopy allows to return 'action'
@@ -1100,10 +1100,10 @@ Launch Manually Once: after having been launched manually, it sets automatically
wizard.write({'state': 'done'})
# Load action
- act_type = self.pool.get('ir.actions.actions').read(cr, uid, wizard.action_id.id, ['type'], context=context)
+ act_type = wizard.action_id.type
- res = self.pool[act_type['type']].read(cr, uid, wizard.action_id.id, [], context=context)
- if act_type['type'] != 'ir.actions.act_window':
+ res = self.pool[act_type].read(cr, uid, [wizard.action_id.id], [], context=context)[0]
+ if act_type != 'ir.actions.act_window':
return res
res.setdefault('context','{}')
res['nodestroy'] = True
diff --git a/openerp/addons/base/ir/ir_attachment.py b/openerp/addons/base/ir/ir_attachment.py
index ef4a6b205a7..ae28a89aa21 100644
--- a/openerp/addons/base/ir/ir_attachment.py
+++ b/openerp/addons/base/ir/ir_attachment.py
@@ -56,12 +56,12 @@ class ir_attachment(osv.osv):
if model_object and res_id:
model_pool = self.pool[model_object]
res = model_pool.name_get(cr,uid,[res_id],context)
- res_name = res and res[0][1] or False
+ res_name = res and res[0][1] or None
if res_name:
field = self._columns.get('res_name',False)
if field and len(res_name) > field.size:
res_name = res_name[:30] + '...'
- data[attachment.id] = res_name
+ data[attachment.id] = res_name or False
else:
data[attachment.id] = False
return data
@@ -273,7 +273,7 @@ class ir_attachment(osv.osv):
# performed in batch as much as possible.
ima = self.pool.get('ir.model.access')
for model, targets in model_attachments.iteritems():
- if not self.pool.get(model):
+ if model not in self.pool:
continue
if not ima.check(cr, uid, model, 'read', False):
# remove all corresponding attachment ids
@@ -297,7 +297,7 @@ class ir_attachment(osv.osv):
if isinstance(ids, (int, long)):
ids = [ids]
self.check(cr, uid, ids, 'read', context=context)
- return super(ir_attachment, self).read(cr, uid, ids, fields_to_read, context, load)
+ return super(ir_attachment, self).read(cr, uid, ids, fields_to_read, context=context, load=load)
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)):
diff --git a/openerp/addons/base/ir/ir_cron.py b/openerp/addons/base/ir/ir_cron.py
index 8350972fa27..60790c8fdc6 100644
--- a/openerp/addons/base/ir/ir_cron.py
+++ b/openerp/addons/base/ir/ir_cron.py
@@ -26,7 +26,7 @@ from datetime import datetime
from dateutil.relativedelta import relativedelta
import openerp
-from openerp import netsvc
+from openerp import SUPERUSER_ID, netsvc, api
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.safe_eval import safe_eval as eval
@@ -149,36 +149,38 @@ class ir_cron(osv.osv):
except Exception, e:
self._handle_callback_exception(cr, uid, model_name, method_name, args, job_id, e)
- def _process_job(self, job_cr, job, cron_cr):
+ def _process_job(self, cr, job, cron_cr):
""" Run a given job taking care of the repetition.
- :param job_cr: cursor to use to execute the job, safe to commit/rollback
+ :param cr: cursor to use to execute the job, safe to commit/rollback
:param job: job to be run (as a dictionary).
:param cron_cr: cursor holding lock on the cron job row, to use to update the next exec date,
must not be committed/rolled back!
"""
try:
- now = datetime.now()
- nextcall = datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT)
- numbercall = job['numbercall']
+ with api.Environment.manage():
+ now = datetime.now()
+ nextcall = datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT)
+ numbercall = job['numbercall']
- ok = False
- while nextcall < now and numbercall:
- if numbercall > 0:
- numbercall -= 1
- if not ok or job['doall']:
- self._callback(job_cr, job['user_id'], job['model'], job['function'], job['args'], job['id'])
- if numbercall:
- nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
- ok = True
- addsql = ''
- if not numbercall:
- addsql = ', active=False'
- cron_cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s",
- (nextcall.strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id']))
+ ok = False
+ while nextcall < now and numbercall:
+ if numbercall > 0:
+ numbercall -= 1
+ if not ok or job['doall']:
+ self._callback(cr, job['user_id'], job['model'], job['function'], job['args'], job['id'])
+ if numbercall:
+ nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
+ ok = True
+ addsql = ''
+ if not numbercall:
+ addsql = ', active=False'
+ cron_cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s",
+ (nextcall.strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id']))
+ self.invalidate_cache(cr, SUPERUSER_ID)
finally:
- job_cr.commit()
+ cr.commit()
cron_cr.commit()
@classmethod
diff --git a/openerp/addons/base/ir/ir_exports.py b/openerp/addons/base/ir/ir_exports.py
index 745ab7cd233..2bdc639024a 100644
--- a/openerp/addons/base/ir/ir_exports.py
+++ b/openerp/addons/base/ir/ir_exports.py
@@ -29,7 +29,7 @@ class ir_exports(osv.osv):
'name': fields.char('Export Name'),
'resource': fields.char('Resource', select=True),
'export_fields': fields.one2many('ir.exports.line', 'export_id',
- 'Export ID'),
+ 'Export ID', copy=True),
}
diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py
index 217ea319d8e..f70d3de33c3 100644
--- a/openerp/addons/base/ir/ir_fields.py
+++ b/openerp/addons/base/ir/ir_fields.py
@@ -31,6 +31,10 @@ LINK_TO = lambda id: (4, id, False)
DELETE_ALL = lambda: (5, False, False)
REPLACE_WITH = lambda ids: (6, False, ids)
+class ImportWarning(Warning):
+ """ Used to send warnings upwards the stack during the import process """
+ pass
+
class ConversionNotFound(ValueError): pass
class ColumnWrapper(object):
@@ -124,7 +128,7 @@ class ir_fields_converter(orm.Model):
If a converter can perform its function but has to make assumptions
about the data, it can send a warning to the user through adding an
- instance of :class:`~openerp.osv.orm.ImportWarning` to the second value
+ instance of :class:`~.ImportWarning` to the second value
it returns. The handling of a warning at the upper levels is the same
as ``ValueError`` above.
@@ -165,7 +169,7 @@ class ir_fields_converter(orm.Model):
))
if value.lower() in falses: return False, []
- return True, [orm.ImportWarning(
+ return True, [ImportWarning(
_(u"Unknown value '%s' for boolean field '%%(field)s', assuming '%s'")
% (value, yes), {
'moreinfo': _(u"Use '1' for yes and '0' for no")
@@ -334,7 +338,7 @@ class ir_fields_converter(orm.Model):
cr, uid, name=value, operator='=', context=context)
if ids:
if len(ids) > 1:
- warnings.append(orm.ImportWarning(
+ warnings.append(ImportWarning(
_(u"Found multiple matches for field '%%(field)s' (%d matches)")
% (len(ids))))
id, _name = ids[0]
diff --git a/openerp/addons/base/ir/ir_http.py b/openerp/addons/base/ir/ir_http.py
index 21c8b7ef941..f2967a02e70 100644
--- a/openerp/addons/base/ir/ir_http.py
+++ b/openerp/addons/base/ir/ir_http.py
@@ -5,9 +5,13 @@ import logging
import re
import sys
-import werkzeug
+import werkzeug.exceptions
+import werkzeug.routing
+import werkzeug.urls
+import werkzeug.utils
import openerp
+import openerp.exceptions
from openerp import http
from openerp.http import request
from openerp.osv import osv, orm
@@ -59,7 +63,7 @@ class ir_http(osv.AbstractModel):
request.uid = request.session.uid
if not request.uid:
if not request.params.get('noredirect'):
- query = werkzeug.url_encode({
+ query = werkzeug.urls.url_encode({
'redirect': request.httprequest.url,
})
response = werkzeug.utils.redirect('/web/login?%s' % query)
diff --git a/openerp/addons/base/ir/ir_mail_server.py b/openerp/addons/base/ir/ir_mail_server.py
index 747f4f1446d..aeded8a1295 100644
--- a/openerp/addons/base/ir/ir_mail_server.py
+++ b/openerp/addons/base/ir/ir_mail_server.py
@@ -19,12 +19,12 @@
#
##############################################################################
-from email.MIMEText import MIMEText
-from email.MIMEBase import MIMEBase
-from email.MIMEMultipart import MIMEMultipart
-from email.Charset import Charset
-from email.Header import Header
-from email.Utils import formatdate, make_msgid, COMMASPACE
+from email.mime.text import MIMEText
+from email.mime.base import MIMEBase
+from email.mime.multipart import MIMEMultipart
+from email.charset import Charset
+from email.header import Header
+from email.utils import formatdate, make_msgid, COMMASPACE
from email import Encoders
import logging
import re
@@ -461,21 +461,18 @@ class ir_mail_server(osv.osv):
mdir.add(message.as_string(True))
return message_id
+ smtp = None
try:
smtp = self.connect(smtp_server, smtp_port, smtp_user, smtp_password, smtp_encryption or False, smtp_debug)
smtp.sendmail(smtp_from, smtp_to_list, message.as_string())
finally:
- try:
- # Close Connection of SMTP Server
+ if smtp is not None:
smtp.quit()
- except Exception:
- # ignored, just a consequence of the previous exception
- pass
except Exception, e:
msg = _("Mail delivery failed via SMTP server '%s'.\n%s: %s") % (tools.ustr(smtp_server),
e.__class__.__name__,
tools.ustr(e))
- _logger.exception(msg)
+ _logger.error(msg)
raise MailDeliveryException(_("Mail Delivery Failed"), msg)
return message_id
diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py
index c759dace9c7..e29b44f0678 100644
--- a/openerp/addons/base/ir/ir_model.py
+++ b/openerp/addons/base/ir/ir_model.py
@@ -28,12 +28,11 @@ import openerp
import openerp.modules.registry
from openerp import SUPERUSER_ID
from openerp import tools
-from openerp.osv import fields,osv
-from openerp.osv.orm import Model, browse_null
-from openerp.tools.safe_eval import safe_eval as eval
+from openerp.osv import fields, osv
+from openerp.osv.orm import BaseModel, Model, MAGIC_COLUMNS, except_orm
from openerp.tools import config
+from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
-from openerp.osv.orm import except_orm, browse_record, MAGIC_COLUMNS
_logger = logging.getLogger(__name__)
@@ -99,7 +98,7 @@ class ir_model(osv.osv):
'name': fields.char('Model Description', translate=True, required=True),
'model': fields.char('Model', required=True, select=1),
'info': fields.text('Information'),
- 'field_id': fields.one2many('ir.model.fields', 'model_id', 'Fields', required=True),
+ 'field_id': fields.one2many('ir.model.fields', 'model_id', 'Fields', required=True, copy=True),
'state': fields.selection([('manual','Custom Object'),('base','Base Object')],'Type', readonly=True),
'access_ids': fields.one2many('ir.model.access', 'model_id', 'Access'),
'osv_memory': fields.function(_is_osv_memory, string='Transient Model', type='boolean',
@@ -133,15 +132,10 @@ class ir_model(osv.osv):
('obj_name_uniq', 'unique (model)', 'Each model must be unique!'),
]
- # overridden to allow searching both on model name (model field)
- # and model description (name field)
- def _name_search(self, cr, uid, name='', args=None, operator='ilike', context=None, limit=100, name_get_uid=None):
- if args is None:
- args = []
- domain = args + ['|', ('model', operator, name), ('name', operator, name)]
- return self.name_get(cr, name_get_uid or uid,
- super(ir_model, self).search(cr, uid, domain, limit=limit, context=context),
- context=context)
+ def _search_display_name(self, operator, value):
+ # overridden to allow searching both on model name (model field) and
+ # model description (name field)
+ return ['|', ('model', operator, value), ('name', operator, value)]
def _drop_table(self, cr, uid, ids, context=None):
for model in self.browse(cr, uid, ids, context):
@@ -177,6 +171,7 @@ class ir_model(osv.osv):
def write(self, cr, user, ids, vals, context=None):
if context:
+ context = dict(context)
context.pop('__last_update', None)
# Filter out operations 4 link from field id, because openerp-web
# always write (4,id,False) even for non dirty items
@@ -207,7 +202,7 @@ class ir_model(osv.osv):
_custom = True
x_custom_model._name = model
x_custom_model._module = False
- a = x_custom_model.create_instance(self.pool, cr)
+ a = x_custom_model._build_model(self.pool, cr)
if not a._columns:
x_name = 'id'
elif 'x_name' in a._columns.keys():
@@ -629,8 +624,8 @@ class ir_model_access(osv.osv):
""" Check if a specific group has the access mode to the specified model"""
assert mode in ['read','write','create','unlink'], 'Invalid access mode'
- if isinstance(model, browse_record):
- assert model._table_name == 'ir.model', 'Invalid model object'
+ if isinstance(model, BaseModel):
+ assert model._name == 'ir.model', 'Invalid model object'
model_name = model.name
else:
model_name = model
@@ -688,8 +683,8 @@ class ir_model_access(osv.osv):
assert mode in ['read','write','create','unlink'], 'Invalid access mode'
- if isinstance(model, browse_record):
- assert model._table_name == 'ir.model', 'Invalid model object'
+ if isinstance(model, BaseModel):
+ assert model._name == 'ir.model', 'Invalid model object'
model_name = model.model
else:
model_name = model
@@ -757,6 +752,7 @@ class ir_model_access(osv.osv):
pass
def call_cache_clearing_methods(self, cr):
+ self.invalidate_cache(cr, SUPERUSER_ID)
self.check.clear_cache(self) # clear the cache of check function
for model, method in self.__cache_clearing_methods:
if model in self.pool:
@@ -765,19 +761,19 @@ class ir_model_access(osv.osv):
#
# Check rights on actions
#
- def write(self, cr, uid, *args, **argv):
+ def write(self, cr, uid, ids, values, context=None):
self.call_cache_clearing_methods(cr)
- res = super(ir_model_access, self).write(cr, uid, *args, **argv)
+ res = super(ir_model_access, self).write(cr, uid, ids, values, context=context)
return res
- def create(self, cr, uid, *args, **argv):
+ def create(self, cr, uid, values, context=None):
self.call_cache_clearing_methods(cr)
- res = super(ir_model_access, self).create(cr, uid, *args, **argv)
+ res = super(ir_model_access, self).create(cr, uid, values, context=context)
return res
- def unlink(self, cr, uid, *args, **argv):
+ def unlink(self, cr, uid, ids, context=None):
self.call_cache_clearing_methods(cr)
- res = super(ir_model_access, self).unlink(cr, uid, *args, **argv)
+ res = super(ir_model_access, self).unlink(cr, uid, ids, context=context)
return res
class ir_model_data(osv.osv):
@@ -833,8 +829,8 @@ class ir_model_data(osv.osv):
'date_init': fields.datetime('Init Date')
}
_defaults = {
- 'date_init': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
- 'date_update': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
+ 'date_init': fields.datetime.now,
+ 'date_update': fields.datetime.now,
'noupdate': False,
'module': ''
}
@@ -844,12 +840,11 @@ class ir_model_data(osv.osv):
def __init__(self, pool, cr):
osv.osv.__init__(self, pool, cr)
- self.doinit = True
# also stored in pool to avoid being discarded along with this osv instance
if getattr(pool, 'model_data_reference_ids', None) is None:
self.pool.model_data_reference_ids = {}
-
- self.loads = self.pool.model_data_reference_ids
+ # put loads on the class, in order to share it among all instances
+ type(self).loads = self.pool.model_data_reference_ids
def _auto_init(self, cr, context=None):
super(ir_model_data, self)._auto_init(cr, context)
@@ -888,7 +883,7 @@ class ir_model_data(osv.osv):
def xmlid_to_object(self, cr, uid, xmlid, raise_if_not_found=False, context=None):
""" Return a browse_record
- if not found and raise_if_not_found is True return the browse_null
+ if not found and raise_if_not_found is True return None
"""
t = self.xmlid_to_res_model_res_id(cr, uid, xmlid, raise_if_not_found)
res_model, res_id = t
@@ -899,7 +894,7 @@ class ir_model_data(osv.osv):
return record
if raise_if_not_found:
raise ValueError('No record found for unique ID %s. It may have been deleted.' % (xml_id))
- return browse_null()
+ return None
# OLD API
def _get_id(self, cr, uid, module, xml_id):
@@ -924,7 +919,7 @@ class ir_model_data(osv.osv):
def get_object(self, cr, uid, module, xml_id, context=None):
""" Returns a browsable record for the given module name and xml_id.
- If not found, raise a ValueError or return a browse_null, depending
+ If not found, raise a ValueError or return None, depending
on the value of `raise_exception`.
"""
return self.xmlid_to_object(cr, uid, "%s.%s" % (module, xml_id), raise_if_not_found=True, context=context)
@@ -961,8 +956,6 @@ class ir_model_data(osv.osv):
if xml_id and ('.' in xml_id):
assert len(xml_id.split('.'))==2, _("'%s' contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id") % xml_id
module, xml_id = xml_id.split('.')
- if (not xml_id) and (not self.doinit):
- return False
action_id = False
if xml_id:
cr.execute('''SELECT imd.id, imd.res_id, md.id, imd.model, imd.noupdate
@@ -1034,8 +1027,8 @@ class ir_model_data(osv.osv):
if xml_id and res_id:
self.loads[(module, xml_id)] = (model, res_id)
for table, inherit_field in model_obj._inherits.iteritems():
- inherit_id = model_obj.read(cr, uid, res_id,
- [inherit_field])[inherit_field]
+ inherit_id = model_obj.read(cr, uid, [res_id],
+ [inherit_field])[0][inherit_field]
self.loads[(module, xml_id + '_' + table.replace('.', '_'))] = (table, inherit_id)
return res_id
@@ -1058,11 +1051,12 @@ class ir_model_data(osv.osv):
cr.execute('select * from ir_values where model=%s and key=%s and name=%s'+where,(model, key, name))
res = cr.fetchone()
+ ir_values_obj = openerp.registry(cr.dbname)['ir.values']
if not res:
- ir_values_obj = openerp.registry(cr.dbname)['ir.values']
ir_values_obj.set(cr, uid, key, key2, name, models, value, replace, isobject, meta)
elif xml_id:
cr.execute('UPDATE ir_values set value=%s WHERE model=%s and key=%s and name=%s'+where,(value, model, key, name))
+ ir_values_obj.invalidate_cache(cr, uid, ['value'])
return True
def _module_data_uninstall(self, cr, uid, modules_to_remove, context=None):
@@ -1104,6 +1098,7 @@ class ir_model_data(osv.osv):
cr.execute('select res_type,res_id from wkf_instance where id IN (select inst_id from wkf_workitem where act_id=%s)', (res_id,))
wkf_todo.extend(cr.fetchall())
cr.execute("update wkf_transition set condition='True', group_id=NULL, signal=NULL,act_to=act_from,act_from=%s where act_to=%s", (res_id,res_id))
+ self.invalidate_cache(cr, uid, context=context)
for model,res_id in wkf_todo:
try:
@@ -1125,7 +1120,7 @@ class ir_model_data(osv.osv):
_logger.info('Deleting orphan external_ids %s', external_ids)
self.unlink(cr, uid, external_ids)
continue
- if field.name in openerp.osv.orm.LOG_ACCESS_COLUMNS and self.pool[field.model]._log_access:
+ if field.name in openerp.models.LOG_ACCESS_COLUMNS and self.pool[field.model]._log_access:
continue
if field.name == 'id':
continue
diff --git a/openerp/addons/base/ir/ir_qweb.py b/openerp/addons/base/ir/ir_qweb.py
index 27822c78246..47866b69eb5 100644
--- a/openerp/addons/base/ir/ir_qweb.py
+++ b/openerp/addons/base/ir/ir_qweb.py
@@ -445,7 +445,7 @@ class QWeb(orm.AbstractModel):
record, field_name = template_attributes["field"].rsplit('.', 1)
record = self.eval_object(record, qwebcontext)
- column = record._model._all_columns[field_name].column
+ column = record._all_columns[field_name].column
options = json.loads(template_attributes.get('field-options') or '{}')
field_type = get_field_type(column, options)
@@ -506,10 +506,10 @@ class FieldConverter(osv.AbstractModel):
:returns: iterable of (attribute name, attribute value) pairs.
"""
- column = record._model._all_columns[field_name].column
+ column = record._all_columns[field_name].column
field_type = get_field_type(column, options)
return [
- ('data-oe-model', record._model._name),
+ ('data-oe-model', record._name),
('data-oe-id', record.id),
('data-oe-field', field_name),
('data-oe-type', field_type),
@@ -541,7 +541,7 @@ class FieldConverter(osv.AbstractModel):
try:
content = self.record_to_html(
cr, uid, field_name, record,
- record._model._all_columns[field_name].column,
+ record._all_columns[field_name].column,
options, context=context)
if options.get('html-escape', True):
content = escape(content)
@@ -549,7 +549,7 @@ class FieldConverter(osv.AbstractModel):
content = content.__html__()
except Exception:
_logger.warning("Could not get field %s for model %s",
- field_name, record._model._name, exc_info=True)
+ field_name, record._name, exc_info=True)
content = None
if context and context.get('inherit_branding'):
@@ -879,7 +879,7 @@ class Contact(orm.AbstractModel):
id = getattr(record, field_name).id
field_browse = self.pool[column._obj].browse(cr, openerp.SUPERUSER_ID, id, context={"show_address": True})
- value = field_browse.name_get()[0][1]
+ value = field_browse.display_name
val = {
'name': value.split("\n")[0],
@@ -888,7 +888,7 @@ class Contact(orm.AbstractModel):
'mobile': field_browse.mobile,
'fax': field_browse.fax,
'city': field_browse.city,
- 'country_id': field_browse.country_id and field_browse.country_id.name_get()[0][1],
+ 'country_id': field_browse.country_id.display_name,
'website': field_browse.website,
'email': field_browse.email,
'fields': opf,
diff --git a/openerp/addons/base/ir/ir_qweb.xml b/openerp/addons/base/ir/ir_qweb.xml
index c69dc17c851..30b366d307c 100644
--- a/openerp/addons/base/ir/ir_qweb.xml
+++ b/openerp/addons/base/ir/ir_qweb.xml
@@ -13,7 +13,9 @@
- ,
+
+ ,
+
diff --git a/openerp/addons/base/ir/ir_rule.py b/openerp/addons/base/ir/ir_rule.py
index 2ac5893038b..c55ad1d5dcd 100644
--- a/openerp/addons/base/ir/ir_rule.py
+++ b/openerp/addons/base/ir/ir_rule.py
@@ -78,7 +78,7 @@ class ir_rule(osv.osv):
'global': fields.function(_get_value, string='Global', type='boolean', store=True, help="If no group is specified the rule is global and applied to everyone"),
'groups': fields.many2many('res.groups', 'rule_group_rel', 'rule_group_id', 'group_id', 'Groups'),
'domain_force': fields.text('Domain'),
- 'domain': fields.function(_domain_force_get, string='Domain', type='text'),
+ 'domain': fields.function(_domain_force_get, string='Domain', type='binary'),
'perm_read': fields.boolean('Apply for Read'),
'perm_write': fields.boolean('Apply for Write'),
'perm_create': fields.boolean('Apply for Create'),
@@ -127,7 +127,7 @@ class ir_rule(osv.osv):
group_domains = {} # map: group -> list of domains
for rule in self.browse(cr, SUPERUSER_ID, rule_ids):
# read 'domain' as UID to have the correct eval context for the rule.
- rule_domain = self.read(cr, uid, rule.id, ['domain'])['domain']
+ rule_domain = self.read(cr, uid, [rule.id], ['domain'])[0]['domain']
dom = expression.normalize_domain(rule_domain)
for group in rule.groups:
if group in user.groups_id:
diff --git a/openerp/addons/base/ir/ir_sequence.py b/openerp/addons/base/ir/ir_sequence.py
index a990a17f032..e589e2cae06 100644
--- a/openerp/addons/base/ir/ir_sequence.py
+++ b/openerp/addons/base/ir/ir_sequence.py
@@ -234,15 +234,15 @@ class ir_sequence(openerp.osv.osv.osv):
'sec': time.strftime('%S', t),
}
- def _next(self, cr, uid, seq_ids, context=None):
- if not seq_ids:
+ def _next(self, cr, uid, ids, context=None):
+ if not ids:
return False
if context is None:
context = {}
force_company = context.get('force_company')
if not force_company:
force_company = self.pool.get('res.users').browse(cr, uid, uid).company_id.id
- sequences = self.read(cr, uid, seq_ids, ['name','company_id','implementation','number_next','prefix','suffix','padding'])
+ sequences = self.read(cr, uid, ids, ['name','company_id','implementation','number_next','prefix','suffix','padding'])
preferred_sequences = [s for s in sequences if s['company_id'] and s['company_id'][0] == force_company ]
seq = preferred_sequences[0] if preferred_sequences else sequences[0]
if seq['implementation'] == 'standard':
@@ -251,6 +251,7 @@ class ir_sequence(openerp.osv.osv.osv):
else:
cr.execute("SELECT number_next FROM ir_sequence WHERE id=%s FOR UPDATE NOWAIT", (seq['id'],))
cr.execute("UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s ", (seq['id'],))
+ self.invalidate_cache(cr, uid, ['number_next'], [seq['id']], context=context)
d = self._interpolation_dict()
try:
interpolated_prefix = self._interpolate(seq['prefix'], d)
diff --git a/openerp/addons/base/ir/ir_translation.py b/openerp/addons/base/ir/ir_translation.py
index 7952c957caf..07732850180 100644
--- a/openerp/addons/base/ir/ir_translation.py
+++ b/openerp/addons/base/ir/ir_translation.py
@@ -168,11 +168,11 @@ class ir_translation(osv.osv):
else:
model_name, field = record.name.split(',')
model = self.pool.get(model_name)
- if model and model.exists(cr, uid, record.res_id, context=context):
+ if model is not None:
# Pass context without lang, need to read real stored field, not translation
context_no_lang = dict(context, lang=None)
- result = model.read(cr, uid, record.res_id, [field], context=context_no_lang)
- res[record.id] = result[field] if result else False
+ result = model.read(cr, uid, [record.res_id], [field], context=context_no_lang)
+ res[record.id] = result[0][field] if result else False
return res
def _set_src(self, cr, uid, id, name, value, args, context=None):
diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py
index 60014be9e01..d55451dd33c 100644
--- a/openerp/addons/base/ir/ir_ui_menu.py
+++ b/openerp/addons/base/ir/ir_ui_menu.py
@@ -21,15 +21,15 @@
##############################################################################
import base64
+import operator
import re
import threading
-import operator
-from openerp.tools.safe_eval import safe_eval as eval
-from openerp import tools
+
import openerp.modules
from openerp.osv import fields, osv
+from openerp import api, tools
+from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
-from openerp import SUPERUSER_ID
MENU_ITEM_SEPARATOR = "/"
@@ -38,71 +38,74 @@ class ir_ui_menu(osv.osv):
_name = 'ir.ui.menu'
def __init__(self, *args, **kwargs):
- self.cache_lock = threading.RLock()
- self._cache = {}
+ cls = type(self)
+ # by design, self._menu_cache is specific to the database
+ cls._menu_cache_lock = threading.RLock()
+ cls._menu_cache = {}
super(ir_ui_menu, self).__init__(*args, **kwargs)
self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache')
def clear_cache(self):
- with self.cache_lock:
+ with self._menu_cache_lock:
# radical but this doesn't frequently happen
- if self._cache:
+ if self._menu_cache:
# Normally this is done by openerp.tools.ormcache
# but since we do not use it, set it by ourself.
self.pool._any_cache_cleared = True
- self._cache = {}
+ self._menu_cache.clear()
- def _filter_visible_menus(self, cr, uid, ids, context=None):
- """Filters the give menu ids to only keep the menu items that should be
- visible in the menu hierarchy of the current user.
- Uses a cache for speeding up the computation.
+ @api.multi
+ @api.returns('self')
+ def _filter_visible_menus(self):
+ """ Filter `self` to only keep the menu items that should be visible in
+ the menu hierarchy of the current user.
+ Uses a cache for speeding up the computation.
"""
- with self.cache_lock:
- modelaccess = self.pool.get('ir.model.access')
- user_groups = set(self.pool.get('res.users').read(cr, SUPERUSER_ID, uid, ['groups_id'])['groups_id'])
- result = []
- for menu in self.browse(cr, uid, ids, context=context):
- # this key works because user access rights are all based on user's groups (cfr ir_model_access.check)
- key = (cr.dbname, menu.id, tuple(user_groups))
- if key in self._cache:
- if self._cache[key]:
- result.append(menu.id)
- #elif not menu.groups_id and not menu.action:
- # result.append(menu.id)
- continue
+ with self._menu_cache_lock:
+ groups = self.env.user.groups_id
- self._cache[key] = False
- if menu.groups_id:
- restrict_to_groups = [g.id for g in menu.groups_id]
- if not user_groups.intersection(restrict_to_groups):
- continue
- #result.append(menu.id)
- #self._cache[key] = True
- #continue
+ # visibility is entirely based on the user's groups;
+ # self._menu_cache[key] gives the ids of all visible menus
+ key = frozenset(groups._ids)
+ if key in self._menu_cache:
+ visible = self.browse(self._menu_cache[key])
- if menu.action:
- # we check if the user has access to the action of the menu
- data = menu.action
- if data:
- model_field = { 'ir.actions.act_window': 'res_model',
- 'ir.actions.report.xml': 'model',
- 'ir.actions.wizard': 'model',
- 'ir.actions.server': 'model_id',
- }
+ else:
+ # retrieve all menus, and determine which ones are visible
+ context = {'ir.ui.menu.full_list': True}
+ menus = self.with_context(context).search([])
- field = model_field.get(menu.action._name)
- if field and data[field]:
- if not modelaccess.check(cr, uid, data[field], 'read', False):
- continue
- else:
- # if there is no action, it's a 'folder' menu
- if not menu.child_id:
- # not displayed if there is no children
- continue
+ # first discard all menus with groups the user does not have
+ menus = menus.filtered(
+ lambda menu: not menu.groups_id or menu.groups_id & groups)
- result.append(menu.id)
- self._cache[key] = True
- return result
+ # take apart menus that have an action
+ action_menus = menus.filtered('action')
+ folder_menus = menus - action_menus
+ visible = self.browse()
+
+ # process action menus, check whether their action is allowed
+ access = self.env['ir.model.access']
+ model_fname = {
+ 'ir.actions.act_window': 'res_model',
+ 'ir.actions.report.xml': 'model',
+ 'ir.actions.wizard': 'model',
+ 'ir.actions.server': 'model_id',
+ }
+ for menu in action_menus:
+ fname = model_fname.get(menu.action._name)
+ if not fname or not menu.action[fname] or \
+ access.check(menu.action[fname], 'read', False):
+ # make menu visible, and its folder ancestors, too
+ visible += menu
+ menu = menu.parent_id
+ while menu and menu in folder_menus and menu not in visible:
+ visible += menu
+ menu = menu.parent_id
+
+ self._menu_cache[key] = visible._ids
+
+ return self.filtered(lambda menu: menu in visible)
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
if context is None:
@@ -155,13 +158,13 @@ class ir_ui_menu(osv.osv):
parent_path = ''
return parent_path + elmt.name
- def create(self, *args, **kwargs):
+ def create(self, cr, uid, values, context=None):
self.clear_cache()
- return super(ir_ui_menu, self).create(*args, **kwargs)
+ return super(ir_ui_menu, self).create(cr, uid, values, context=context)
- def write(self, *args, **kwargs):
+ def write(self, cr, uid, ids, values, context=None):
self.clear_cache()
- return super(ir_ui_menu, self).write(*args, **kwargs)
+ return super(ir_ui_menu, self).write(cr, uid, ids, values, context=context)
def unlink(self, cr, uid, ids, context=None):
# Detach children and promote them to top-level, because it would be unwise to
@@ -182,7 +185,7 @@ class ir_ui_menu(osv.osv):
def copy(self, cr, uid, id, default=None, context=None):
ir_values_obj = self.pool.get('ir.values')
- res = super(ir_ui_menu, self).copy(cr, uid, id, context=context)
+ res = super(ir_ui_menu, self).copy(cr, uid, id, default=default, context=context)
datas=self.read(cr,uid,[res],['name'])[0]
rex=re.compile('\([0-9]+\)')
concat=rex.findall(datas['name'])
diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py
index b5231808b5e..08fb4d4ef59 100644
--- a/openerp/addons/base/ir/ir_ui_view.py
+++ b/openerp/addons/base/ir/ir_ui_view.py
@@ -35,7 +35,7 @@ import HTMLParser
from lxml import etree
import openerp
-from openerp import tools
+from openerp import tools, api
from openerp.http import request
from openerp.osv import fields, osv, orm
from openerp.tools import graph, SKIPPED_ELEMENT_TYPES
@@ -302,15 +302,6 @@ class view(osv.osv):
view.application,
))
-
- def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({
- 'model_ids': [],
- })
- return super(view, self).copy(cr, uid, id, default, context=context)
-
# default view selection
def default_view(self, cr, uid, model, view_type, context=None):
""" Fetches the default view for the provided (model, view_type) pair:
@@ -603,7 +594,7 @@ class view(osv.osv):
modifiers = {}
Model = self.pool.get(model)
- if not Model:
+ if Model is None:
self.raise_view_error(cr, user, _('Model not found: %(model)s') % dict(model=model),
view_id, context)
@@ -622,10 +613,10 @@ class view(osv.osv):
:return: True if field should be included in the result of fields_view_get
"""
- if node.tag == 'field' and node.get('name') in Model._all_columns:
- column = Model._all_columns[node.get('name')].column
- if column.groups and not self.user_has_groups(
- cr, user, groups=column.groups, context=context):
+ if node.tag == 'field' and node.get('name') in Model._fields:
+ field = Model._fields[node.get('name')]
+ if field.groups and not self.user_has_groups(
+ cr, user, groups=field.groups, context=context):
node.getparent().remove(node)
fields.pop(node.get('name'), None)
# no point processing view-level ``groups`` anymore, return
@@ -662,15 +653,8 @@ class view(osv.osv):
fields = xfields
if node.get('name'):
attrs = {}
- try:
- if node.get('name') in Model._columns:
- column = Model._columns[node.get('name')]
- else:
- column = Model._inherit_fields[node.get('name')][2]
- except Exception:
- column = False
-
- if column:
+ field = Model._fields.get(node.get('name'))
+ if field:
children = False
views = {}
for f in node:
@@ -678,7 +662,7 @@ class view(osv.osv):
node.remove(f)
ctx = context.copy()
ctx['base_model_name'] = model
- xarch, xfields = self.postprocess_and_fields(cr, user, column._obj or None, f, view_id, ctx)
+ xarch, xfields = self.postprocess_and_fields(cr, user, field.comodel_name, f, view_id, ctx)
views[str(f.tag)] = {
'arch': xarch,
'fields': xfields
@@ -746,6 +730,36 @@ class view(osv.osv):
orm.transfer_modifiers_to_node(modifiers, node)
return fields
+ def add_on_change(self, cr, user, model_name, arch):
+ """ Add attribute on_change="1" on fields that are dependencies of
+ computed fields on the same view.
+ """
+ # map each field object to its corresponding nodes in arch
+ field_nodes = collections.defaultdict(list)
+
+ def collect(node, model):
+ if node.tag == 'field':
+ field = model._fields.get(node.get('name'))
+ if field:
+ field_nodes[field].append(node)
+ if field.relational:
+ model = self.pool.get(field.comodel_name)
+ for child in node:
+ collect(child, model)
+
+ collect(arch, self.pool[model_name])
+
+ for field, nodes in field_nodes.iteritems():
+ # if field should trigger an onchange, add on_change="1" on the
+ # nodes referring to field
+ model = self.pool[field.model_name]
+ if model._has_onchange(field, field_nodes):
+ for node in nodes:
+ if not node.get('on_change'):
+ node.set('on_change', '1')
+
+ return arch
+
def _disable_workflow_buttons(self, cr, user, model, node):
""" Set the buttons in node to readonly if the user can't activate them. """
if model is None or user == 1:
@@ -784,7 +798,7 @@ class view(osv.osv):
"""
fields = {}
Model = self.pool.get(model)
- if not Model:
+ if Model is None:
self.raise_view_error(cr, user, _('Model not found: %(model)s') % dict(model=model), view_id, context)
if node.tag == 'diagram':
@@ -800,6 +814,7 @@ class view(osv.osv):
else:
fields = Model.fields_get(cr, user, None, context)
+ node = self.add_on_change(cr, user, model, node)
fields_def = self.postprocess(cr, user, model, node, view_id, False, fields, context=context)
node = self._disable_workflow_buttons(cr, user, model, node)
if node.tag in ('kanban', 'tree', 'form', 'gantt'):
@@ -965,6 +980,7 @@ class view(osv.osv):
xmlid = imd.search_read(cr, uid, domain, ['module', 'name'])[0]
return '%s.%s' % (xmlid['module'], xmlid['name'])
+ @api.cr_uid_ids_context
def render(self, cr, uid, id_or_xml_id, values=None, engine='ir.qweb', context=None):
if isinstance(id_or_xml_id, list):
id_or_xml_id = id_or_xml_id[0]
diff --git a/openerp/addons/base/ir/ir_values.py b/openerp/addons/base/ir/ir_values.py
index 41d781ac68f..4720f601f49 100644
--- a/openerp/addons/base/ir/ir_values.py
+++ b/openerp/addons/base/ir/ir_values.py
@@ -20,6 +20,7 @@
##############################################################################
import pickle
+from openerp import tools
from openerp.osv import osv, fields
from openerp.osv.orm import except_orm
@@ -188,6 +189,21 @@ class ir_values(osv.osv):
if not cr.fetchone():
cr.execute('CREATE INDEX ir_values_key_model_key2_res_id_user_id_idx ON ir_values (key, model, key2, res_id, user_id)')
+ def create(self, cr, uid, vals, context=None):
+ res = super(ir_values, self).create(cr, uid, vals, context=context)
+ self.get_defaults_dict.clear_cache(self)
+ return res
+
+ def write(self, cr, uid, ids, vals, context=None):
+ res = super(ir_values, self).write(cr, uid, ids, vals, context=context)
+ self.get_defaults_dict.clear_cache(self)
+ return res
+
+ def unlink(self, cr, uid, ids, context=None):
+ res = super(ir_values, self).unlink(cr, uid, ids, context=context)
+ self.get_defaults_dict.clear_cache(self)
+ return res
+
def set_default(self, cr, uid, model, field_name, value, for_all_users=True, company_id=False, condition=False):
"""Defines a default value for the given model and field_name. Any previous
default for the same scope (model, field_name, value, for_all_users, company_id, condition)
@@ -319,6 +335,15 @@ class ir_values(osv.osv):
(row['id'], row['name'], pickle.loads(row['value'].encode('utf-8'))))
return defaults.values()
+ # use ormcache: this is called a lot by BaseModel.add_default_value()!
+ @tools.ormcache(skiparg=2)
+ def get_defaults_dict(self, cr, uid, model, condition=False):
+ """ Returns a dictionary mapping field names with their corresponding
+ default value. This method simply improves the returned value of
+ :meth:`~.get_defaults`.
+ """
+ return dict((f, v) for i, f, v in self.get_defaults(cr, uid, model, condition))
+
def set_action(self, cr, uid, name, action_slot, model, action, res_id=False):
"""Binds an the given action to the given model's action slot - for later
retrieval via :meth:`~.get_actions`. Any existing binding of the same action
@@ -395,9 +420,9 @@ class ir_values(osv.osv):
if not action['value']:
continue # skip if undefined
action_model_name, action_id = action['value'].split(',')
- action_model = self.pool.get(action_model_name)
- if not action_model:
+ if action_model_name not in self.pool:
continue # unknow model? skip it
+ action_model = self.pool[action_model_name]
fields = [field for field in action_model._all_columns if field not in EXCLUDED_FIELDS]
# FIXME: needs cleanup
try:
diff --git a/openerp/addons/base/module/module.py b/openerp/addons/base/module/module.py
index d0cb6b18f7b..89f3e9d006e 100644
--- a/openerp/addons/base/module/module.py
+++ b/openerp/addons/base/module/module.py
@@ -48,7 +48,8 @@ from openerp.modules.db import create_categories
from openerp.modules import get_module_resource
from openerp.tools.parse_version import parse_version
from openerp.tools.translate import _
-from openerp.osv import fields, osv, orm
+from openerp.osv import osv, orm, fields
+from openerp import api, fields as fields2
_logger = logging.getLogger(__name__)
@@ -374,34 +375,41 @@ class module(osv.osv):
msg = _('Unable to process module "%s" because an external dependency is not met: %s')
raise orm.except_orm(_('Error'), msg % (module_name, e.args[0]))
- def state_update(self, cr, uid, ids, newstate, states_to_update, context=None, level=100):
+ @api.multi
+ def state_update(self, newstate, states_to_update, level=100):
if level < 1:
raise orm.except_orm(_('Error'), _('Recursion error in modules dependencies !'))
+
+ # whether some modules are installed with demo data
demo = False
- for module in self.browse(cr, uid, ids, context=context):
- mdemo = False
+
+ for module in self:
+ # determine dependency modules to update/others
+ update_mods, ready_mods = self.browse(), self.browse()
for dep in module.dependencies_id:
if dep.state == 'unknown':
raise orm.except_orm(_('Error'), _("You try to install module '%s' that depends on module '%s'.\nBut the latter module is not available in your system.") % (module.name, dep.name,))
- ids2 = self.search(cr, uid, [('name', '=', dep.name)])
- if dep.state != newstate:
- mdemo = self.state_update(cr, uid, ids2, newstate, states_to_update, context, level - 1) or mdemo
+ if dep.depend_id.state == newstate:
+ ready_mods += dep.depend_id
else:
- od = self.browse(cr, uid, ids2)[0]
- mdemo = od.demo or mdemo
+ update_mods += dep.depend_id
+ # update dependency modules that require it, and determine demo for module
+ update_demo = update_mods.state_update(newstate, states_to_update, level=level-1)
+ module_demo = module.demo or update_demo or any(mod.demo for mod in ready_mods)
+ demo = demo or module_demo
+
+ # check dependencies and update module itself
self.check_external_dependencies(module.name, newstate)
- if not module.dependencies_id:
- mdemo = module.demo
if module.state in states_to_update:
- self.write(cr, uid, [module.id], {'state': newstate, 'demo': mdemo})
- demo = demo or mdemo
+ module.write({'state': newstate, 'demo': module_demo})
+
return demo
def button_install(self, cr, uid, ids, context=None):
# Mark the given modules to be installed.
- self.state_update(cr, uid, ids, 'to install', ['uninstalled'], context)
+ self.state_update(cr, uid, ids, 'to install', ['uninstalled'], context=context)
# Mark (recursively) the newly satisfied modules to also be installed
@@ -524,7 +532,7 @@ class module(osv.osv):
def button_upgrade(self, cr, uid, ids, context=None):
depobj = self.pool.get('ir.module.module.dependency')
- todo = self.browse(cr, uid, ids, context=context)
+ todo = list(self.browse(cr, uid, ids, context=context))
self.update_list(cr, uid)
i = 0
@@ -598,7 +606,7 @@ class module(osv.osv):
for key in values:
old = getattr(mod, key)
updated = isinstance(values[key], basestring) and tools.ustr(values[key]) or values[key]
- if not old == updated:
+ if (old or updated) and updated != old:
updated_values[key] = values[key]
if terp.get('installable', True) and mod.state == 'uninstallable':
updated_values['state'] = 'uninstalled'
@@ -726,6 +734,7 @@ class module(osv.osv):
cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%s, %s)', (mod_browse.id, dep))
for dep in (existing - needed):
cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s and name = %s', (mod_browse.id, dep))
+ self.invalidate_cache(cr, uid, ['dependencies_id'], [mod_browse.id])
def _update_category(self, cr, uid, mod_browse, category='Uncategorized'):
current_category = mod_browse.category_id
@@ -754,37 +763,47 @@ class module(osv.osv):
if not mod.description:
_logger.warning('module %s: description is empty !', mod.name)
-class module_dependency(osv.osv):
+
+DEP_STATES = [
+ ('uninstallable', 'Uninstallable'),
+ ('uninstalled', 'Not Installed'),
+ ('installed', 'Installed'),
+ ('to upgrade', 'To be upgraded'),
+ ('to remove', 'To be removed'),
+ ('to install', 'To be installed'),
+ ('unknown', 'Unknown'),
+]
+
+class module_dependency(osv.Model):
_name = "ir.module.module.dependency"
_description = "Module dependency"
- def _state(self, cr, uid, ids, name, args, context=None):
- result = {}
- mod_obj = self.pool.get('ir.module.module')
- for md in self.browse(cr, uid, ids):
- ids = mod_obj.search(cr, uid, [('name', '=', md.name)])
- if ids:
- result[md.id] = mod_obj.read(cr, uid, [ids[0]], ['state'])[0]['state']
- else:
- result[md.id] = 'unknown'
- return result
+ # the dependency name
+ name = fields2.Char(index=True)
- _columns = {
- # The dependency name
- 'name': fields.char('Name', select=True),
+ # the module that depends on it
+ module_id = fields2.Many2one('ir.module.module', 'Module', ondelete='cascade')
- # The module that depends on it
- 'module_id': fields.many2one('ir.module.module', 'Module', select=True, ondelete='cascade'),
+ # the module corresponding to the dependency, and its status
+ depend_id = fields2.Many2one('ir.module.module', 'Dependency', compute='_compute_depend')
+ state = fields2.Selection(DEP_STATES, string='Status', compute='_compute_state')
+
+ @api.multi
+ @api.depends('name')
+ def _compute_depend(self):
+ # retrieve all modules corresponding to the dependency names
+ names = list(set(dep.name for dep in self))
+ mods = self.env['ir.module.module'].search([('name', 'in', names)])
+
+ # index modules by name, and assign dependencies
+ name_mod = dict((mod.name, mod) for mod in mods)
+ for dep in self:
+ dep.depend_id = name_mod.get(dep.name)
+
+ @api.one
+ @api.depends('depend_id.state')
+ def _compute_state(self):
+ self.state = self.depend_id.state or 'unknown'
- 'state': fields.function(_state, type='selection', selection=[
- ('uninstallable', 'Uninstallable'),
- ('uninstalled', 'Not Installed'),
- ('installed', 'Installed'),
- ('to upgrade', 'To be upgraded'),
- ('to remove', 'To be removed'),
- ('to install', 'To be installed'),
- ('unknown', 'Unknown'),
- ], string='Status', readonly=True, select=True),
- }
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/module/wizard/base_import_language.py b/openerp/addons/base/module/wizard/base_import_language.py
index d8757605d64..bd554a47c5c 100644
--- a/openerp/addons/base/module/wizard/base_import_language.py
+++ b/openerp/addons/base/module/wizard/base_import_language.py
@@ -44,7 +44,7 @@ class base_language_import(osv.osv_memory):
context = {}
this = self.browse(cr, uid, ids[0])
if this.overwrite:
- context.update(overwrite=True)
+ context = dict(context, overwrite=True)
fileobj = TemporaryFile('w+')
try:
fileobj.write(base64.decodestring(this.data))
diff --git a/openerp/addons/base/module/wizard/base_module_update.py b/openerp/addons/base/module/wizard/base_module_update.py
index e2d8ce01d4a..2f3d0cc1af2 100644
--- a/openerp/addons/base/module/wizard/base_module_update.py
+++ b/openerp/addons/base/module/wizard/base_module_update.py
@@ -1,49 +1,23 @@
# -*- 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 .
-#
-##############################################################################
-from openerp.osv import osv, fields
-
-class base_module_update(osv.osv_memory):
- """ Update Module """
+from openerp import models, fields, api
+class base_module_update(models.TransientModel):
_name = "base.module.update"
_description = "Update Module"
- _columns = {
- 'update': fields.integer('Number of modules updated', readonly=True),
- 'add': fields.integer('Number of modules added', readonly=True),
- 'state':fields.selection([('init','init'),('done','done')], 'Status', readonly=True),
- }
+ updated = fields.Integer('Number of modules updated', readonly=True)
+ added = fields.Integer('Number of modules added', readonly=True)
+ state = fields.Selection([('init', 'init'), ('done', 'done')], 'Status', readonly=True, default='init')
- _defaults = {
- 'state': 'init',
- }
-
- def update_module(self, cr, uid, ids, context=None):
- module_obj = self.pool.get('ir.module.module')
- update, add = module_obj.update_list(cr, uid,)
- self.write(cr, uid, ids, {'update': update, 'add': add, 'state': 'done'}, context=context)
+ @api.one
+ def update_module(self):
+ self.updated, self.added = self.env['ir.module.module'].update_list()
+ self.state = 'done'
return False
- def action_module_open(self, cr, uid, ids, context):
+ @api.multi
+ def action_module_open(self):
res = {
'domain': str([]),
'name': 'Modules',
@@ -54,5 +28,3 @@ class base_module_update(osv.osv_memory):
'type': 'ir.actions.act_window',
}
return res
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/module/wizard/base_module_update_view.xml b/openerp/addons/base/module/wizard/base_module_update_view.xml
index 3dccc061593..eff8e790677 100644
--- a/openerp/addons/base/module/wizard/base_module_update_view.xml
+++ b/openerp/addons/base/module/wizard/base_module_update_view.xml
@@ -13,8 +13,8 @@
-
-
+
+
--
+
-
diff --git a/addons/website_blog/wizard/document_page_create_menu.py b/addons/website_blog/wizard/document_page_create_menu.py
deleted file mode 100644
index 9f20fab0d99..00000000000
--- a/addons/website_blog/wizard/document_page_create_menu.py
+++ /dev/null
@@ -1,86 +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 .
-#
-##############################################################################
-
-from openerp.osv import fields, osv
-
-class document_page_create_menu(osv.osv_memory):
- """ Create Menu """
- _name = "document.page.create.menu"
- _description = "Wizard Create Menu"
-
- _columns = {
- 'menu_name': fields.char('Menu Name', size=256, required=True),
- 'menu_parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', required=True),
- }
-
- def default_get(self, cr, uid, fields, context=None):
- if context is None:
- context = {}
- res = super(document_page_create_menu,self).default_get(cr, uid, fields, context=context)
- page_id = context.get('active_id')
- obj_page = self.pool.get('document.page')
- page = obj_page.browse(cr, uid, page_id, context=context)
- res['menu_name'] = page.name
- return res
-
- def document_page_menu_create(self, cr, uid, ids, context=None):
- if context is None:
- context = {}
- obj_page = self.pool.get('document.page')
- obj_view = self.pool.get('ir.ui.view')
- obj_menu = self.pool.get('ir.ui.menu')
- obj_action = self.pool.get('ir.actions.act_window')
- page_id = context.get('active_id', False)
- page = obj_page.browse(cr, uid, page_id, context=context)
-
- datas = self.browse(cr, uid, ids, context=context)
- data = False
- if datas:
- data = datas[0]
- if not data:
- return {}
- value = {
- 'name': 'Document Page',
- 'view_type': 'form',
- 'view_mode': 'form,tree',
- 'res_model': 'document.page',
- 'view_id': False,
- 'type': 'ir.actions.act_window',
- 'target': 'inlineview',
- }
- value['domain'] = "[('parent_id','=',%d)]" % (page.id)
- value['res_id'] = page.id
-
- action_id = obj_action.create(cr, uid, value)
- menu_id = obj_menu.create(cr, uid, {
- 'name': data.menu_name,
- 'parent_id':data.menu_parent_id.id,
- 'icon': 'STOCK_DIALOG_QUESTION',
- 'action': 'ir.actions.act_window,'+ str(action_id),
- }, context)
- obj_page.write(cr, uid, [page_id], {'menu_id':menu_id})
- return {
- 'type': 'ir.actions.client',
- 'tag': 'reload',
- }
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/website_blog/wizard/document_page_create_menu_view.xml b/addons/website_blog/wizard/document_page_create_menu_view.xml
deleted file mode 100644
index d0106faf6e8..00000000000
--- a/addons/website_blog/wizard/document_page_create_menu_view.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
- Create Menu
- document.page.create.menu
-
-
-
-
-
-
-
-
-
-
-
-
- Create Menu
- ir.actions.act_window
- document.page.create.menu
- form
- form
- new
-
-
-
-
diff --git a/addons/website_crm/controllers/main.py b/addons/website_crm/controllers/main.py
index 37d661e8a04..9b47140b9e3 100644
--- a/addons/website_crm/controllers/main.py
+++ b/addons/website_crm/controllers/main.py
@@ -1,13 +1,12 @@
# -*- coding: utf-8 -*-
import base64
-from openerp.tools.translate import _
-from openerp.addons.web import http
-from openerp.addons.web.http import request
-from openerp import SUPERUSER_ID
-
+import werkzeug
import werkzeug.urls
+from openerp import http, SUPERUSER_ID
+from openerp.http import request
+from openerp.tools.translate import _
class contactus(http.Controller):
diff --git a/addons/website_crm_partner_assign/controllers/main.py b/addons/website_crm_partner_assign/controllers/main.py
index ca08478d8d1..c104b26ce27 100644
--- a/addons/website_crm_partner_assign/controllers/main.py
+++ b/addons/website_crm_partner_assign/controllers/main.py
@@ -109,7 +109,7 @@ class WebsiteCrmPartnerAssign(http.Controller):
context=request.context) # todo in trunk: order="grade_id DESC, implemented_count DESC", offset=pager['offset'], limit=self._references_per_page
partners = partner_obj.browse(request.cr, SUPERUSER_ID, partner_ids, request.context)
# remove me in trunk
- partners.sort(key=lambda x: (-1 * (x.grade_id and x.grade_id.id or 0), len(x.implemented_partner_ids)), reverse=True)
+ partners = sorted(partners, key=lambda x: (-1 * (x.grade_id and x.grade_id.id or 0), len(x.implemented_partner_ids)), reverse=True)
partners = partners[pager['offset']:pager['offset'] + self._references_per_page]
google_map_partner_ids = ','.join(map(str, [p.id for p in partners]))
diff --git a/addons/website_crm_partner_assign/models/res_partner.py b/addons/website_crm_partner_assign/models/res_partner.py
index cc00f8bd6e1..44e45e235fb 100644
--- a/addons/website_crm_partner_assign/models/res_partner.py
+++ b/addons/website_crm_partner_assign/models/res_partner.py
@@ -4,5 +4,5 @@ from openerp.osv import osv, fields
class res_partner_grade(osv.osv):
_inherit = 'res.partner.grade'
_columns = {
- 'website_published': fields.boolean('Published On Website'),
+ 'website_published': fields.boolean('Published On Website', copy=False),
}
diff --git a/addons/website_event/models/event.py b/addons/website_event/models/event.py
index 33eee7c7829..c13ba88a17f 100644
--- a/addons/website_event/models/event.py
+++ b/addons/website_event/models/event.py
@@ -89,7 +89,7 @@ class event(osv.osv):
_columns = {
'twitter_hashtag': fields.char('Twitter Hashtag'),
- 'website_published': fields.boolean('Visible in Website'),
+ 'website_published': fields.boolean('Visible in Website', copy=False),
# TDE TODO FIXME: when website_mail/mail_thread.py inheritance work -> this field won't be necessary
'website_message_ids': fields.one2many(
'mail.message', 'res_id',
diff --git a/addons/website_event_sale/models/sale_order.py b/addons/website_event_sale/models/sale_order.py
index 4c1a83681ea..7351279eee0 100644
--- a/addons/website_event_sale/models/sale_order.py
+++ b/addons/website_event_sale/models/sale_order.py
@@ -30,7 +30,7 @@ class sale_order(osv.Model):
else:
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
if product.event_ticket_ids:
- event_ticket_id = product.event_ticket_ids[0]
+ event_ticket_id = product.event_ticket_ids[0].id
if event_ticket_id:
ticket = self.pool.get('event.event.ticket').browse(cr, uid, event_ticket_id, context=context)
diff --git a/addons/website_event_track/models/event.py b/addons/website_event_track/models/event.py
index 954598f0bd2..657e73d946f 100644
--- a/addons/website_event_track/models/event.py
+++ b/addons/website_event_track/models/event.py
@@ -86,7 +86,7 @@ class event_track(osv.osv):
'event_id': fields.many2one('event.event', 'Event', required=True),
'color': fields.integer('Color Index'),
'priority': fields.selection([('3','Low'),('2','Medium (*)'),('1','High (**)'),('0','Highest (***)')], 'Priority', required=True),
- 'website_published': fields.boolean('Available in the website'),
+ 'website_published': fields.boolean('Available in the website', copy=False),
'website_url': fields.function(_website_url, string="Website url", type="char"),
'image': fields.related('speaker_ids', 'image', type='binary', readonly=True)
}
@@ -120,7 +120,8 @@ class event_track(osv.osv):
#
class event_event(osv.osv):
_inherit = "event.event"
- def _tz_get(self,cr,uid, context=None):
+
+ def _list_tz(self,cr,uid, context=None):
# put POSIX 'Etc/*' entries at the end to avoid confusing users - see bug 1086728
return [(tz,tz) for tz in sorted(pytz.all_timezones, key=lambda tz: tz if not tz.startswith('Etc/') else '_')]
@@ -140,8 +141,8 @@ class event_event(osv.osv):
_columns = {
'tag_ids': fields.many2many('event.tag', string='Tags'),
- 'track_ids': fields.one2many('event.track', 'event_id', 'Tracks'),
- 'sponsor_ids': fields.one2many('event.sponsor', 'event_id', 'Sponsorships'),
+ 'track_ids': fields.one2many('event.track', 'event_id', 'Tracks', copy=True),
+ 'sponsor_ids': fields.one2many('event.sponsor', 'event_id', 'Sponsorships', copy=True),
'blog_id': fields.many2one('blog.blog', 'Event Blog'),
'show_track_proposal': fields.boolean('Talks Proposals'),
'show_tracks': fields.boolean('Multiple Tracks'),
@@ -149,7 +150,7 @@ class event_event(osv.osv):
'count_tracks': fields.function(_count_tracks, type='integer', string='Tracks'),
'tracks_tag_ids': fields.function(_get_tracks_tag_ids, type='one2many', relation='event.track.tag', string='Tags of Tracks'),
'allowed_track_tag_ids': fields.many2many('event.track.tag', string='Accepted Tags', help="List of available tags for track proposals."),
- 'timezone_of_event': fields.selection(_tz_get, 'Event Timezone', size=64),
+ 'timezone_of_event': fields.selection(_list_tz, 'Event Timezone', size=64),
}
_defaults = {
diff --git a/addons/website_forum_doc/controllers/main.py b/addons/website_forum_doc/controllers/main.py
index 2b951ee978d..71563d78f01 100644
--- a/addons/website_forum_doc/controllers/main.py
+++ b/addons/website_forum_doc/controllers/main.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
-from openerp.addons.web import http
-from openerp.addons.web.http import request
+from openerp import http
+from openerp.http import request
from openerp.addons.website.models.website import slug
diff --git a/addons/website_gengo/controllers/main.py b/addons/website_gengo/controllers/main.py
index f80c7cbb34b..f373c9375a3 100644
--- a/addons/website_gengo/controllers/main.py
+++ b/addons/website_gengo/controllers/main.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
import openerp
-from openerp.addons.web import http
-from openerp.addons.web.http import request
+from openerp import http
+from openerp.http import request
import time
GENGO_DEFAULT_LIMIT = 20
diff --git a/addons/website_hr/controllers/main.py b/addons/website_hr/controllers/main.py
index fcc66e6bae8..430c47b5dfa 100644
--- a/addons/website_hr/controllers/main.py
+++ b/addons/website_hr/controllers/main.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from openerp.addons.web import http
-from openerp.addons.web.http import request
+from openerp import http
+from openerp.http import request
class website_hr(http.Controller):
diff --git a/addons/website_hr/models/hr.py b/addons/website_hr/models/hr.py
index 4cacc56dba4..2927993a6d4 100644
--- a/addons/website_hr/models/hr.py
+++ b/addons/website_hr/models/hr.py
@@ -6,7 +6,7 @@ from openerp.osv import osv, fields
class hr(osv.osv):
_inherit = 'hr.employee'
_columns = {
- 'website_published': fields.boolean('Available in the website'),
+ 'website_published': fields.boolean('Available in the website', copy=False),
'public_info': fields.text('Public Info'),
}
_defaults = {
diff --git a/addons/website_hr_recruitment/controllers/main.py b/addons/website_hr_recruitment/controllers/main.py
index 84a0ec93454..c99c6644f9c 100644
--- a/addons/website_hr_recruitment/controllers/main.py
+++ b/addons/website_hr_recruitment/controllers/main.py
@@ -2,9 +2,9 @@
import base64
from openerp import SUPERUSER_ID
-from openerp.addons.web import http
+from openerp import http
from openerp.tools.translate import _
-from openerp.addons.web.http import request
+from openerp.http import request
class website_hr_recruitment(http.Controller):
@http.route([
diff --git a/addons/website_hr_recruitment/models/hr_job.py b/addons/website_hr_recruitment/models/hr_job.py
index 26aad8ae1b8..b14361db333 100644
--- a/addons/website_hr_recruitment/models/hr_job.py
+++ b/addons/website_hr_recruitment/models/hr_job.py
@@ -17,7 +17,7 @@ class hr_job(osv.osv):
return super(hr_job, self).job_open(cr, uid, ids, context)
_columns = {
- 'website_published': fields.boolean('Published'),
+ 'website_published': fields.boolean('Published', copy=False),
'website_description': fields.html('Website description'),
'website_url': fields.function(_website_url, string="Website URL", type="char"),
}
diff --git a/addons/website_mail/controllers/email_designer.py b/addons/website_mail/controllers/email_designer.py
index a9462f7be7d..0a3e1043fad 100644
--- a/addons/website_mail/controllers/email_designer.py
+++ b/addons/website_mail/controllers/email_designer.py
@@ -17,6 +17,7 @@ class WebsiteEmailDesigner(http.Controller):
'email' not in model_cols and 'email_from' not in model_cols or \
'name' not in model_cols and 'subject' not in model_cols:
return request.redirect('/')
+ res_id = int(res_id)
obj_ids = request.registry[model].exists(request.cr, request.uid, [res_id], context=request.context)
if not obj_ids:
return request.redirect('/')
@@ -33,7 +34,6 @@ class WebsiteEmailDesigner(http.Controller):
body_field = 'body_html'
cr, uid, context = request.cr, request.uid, request.context
- res_id = int(res_id)
record = request.registry[model].browse(cr, uid, res_id, context=context)
values = {
diff --git a/addons/website_mail/models/mail_message.py b/addons/website_mail/models/mail_message.py
index cff950f2962..4206872199c 100644
--- a/addons/website_mail/models/mail_message.py
+++ b/addons/website_mail/models/mail_message.py
@@ -43,7 +43,7 @@ class MailMessage(osv.Model):
help='Message description: either the subject, or the beginning of the body'
),
'website_published': fields.boolean(
- 'Published', help="Visible on the website as a comment"
+ 'Published', help="Visible on the website as a comment", copy=False,
),
}
diff --git a/addons/website_mail/models/mail_thread.py b/addons/website_mail/models/mail_thread.py
index bea9178a335..cb19b52cf66 100644
--- a/addons/website_mail/models/mail_thread.py
+++ b/addons/website_mail/models/mail_thread.py
@@ -36,8 +36,3 @@ class MailThread(osv.AbstractModel):
),
}
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default['website_message_ids'] = []
- return super(MailThread, self).copy(cr, uid, id, default=default, context=context)
\ No newline at end of file
diff --git a/addons/website_membership/controllers/main.py b/addons/website_membership/controllers/main.py
index 52ba31324f0..9947eab4d98 100644
--- a/addons/website_membership/controllers/main.py
+++ b/addons/website_membership/controllers/main.py
@@ -73,7 +73,7 @@ class WebsiteMembership(http.Controller):
# displayed membership lines
membership_line_ids = membership_line_obj.search(cr, uid, line_domain, context=context)
membership_lines = membership_line_obj.browse(cr, uid, membership_line_ids, context=context)
- membership_lines.sort(key=lambda x: x.membership_id.website_sequence)
+ membership_lines = sorted(membership_lines, key=lambda x: x.membership_id.website_sequence)
partner_ids = [m.partner.id for m in membership_lines]
google_map_partner_ids = ",".join(map(str, partner_ids))
diff --git a/addons/website_membership/models/product.py b/addons/website_membership/models/product.py
index c45a7a568a5..1de254de78b 100644
--- a/addons/website_membership/models/product.py
+++ b/addons/website_membership/models/product.py
@@ -25,7 +25,7 @@ class product_template(osv.Model):
_inherit = 'product.template'
_columns = {
- 'website_published': fields.boolean('Available in the website'),
+ 'website_published': fields.boolean('Available in the website', copy=False),
}
_defaults = {
'website_published': False,
diff --git a/addons/website_partner/models/res_partner.py b/addons/website_partner/models/res_partner.py
index e762455debe..08fc29ce75f 100644
--- a/addons/website_partner/models/res_partner.py
+++ b/addons/website_partner/models/res_partner.py
@@ -12,7 +12,7 @@ class WebsiteResPartner(osv.Model):
_columns = {
'website_published': fields.boolean(
- 'Publish', help="Publish on the website"),
+ 'Publish', help="Publish on the website", copy=False),
'website_description': fields.html(
'Website Partner Full Description'
),
diff --git a/addons/website_quote/models/order.py b/addons/website_quote/models/order.py
index aaac80ceb5f..c7ae4bc8ab5 100644
--- a/addons/website_quote/models/order.py
+++ b/addons/website_quote/models/order.py
@@ -32,9 +32,9 @@ class sale_quote_template(osv.osv):
_columns = {
'name': fields.char('Quotation Template', required=True),
'website_description': fields.html('Description', translate=True),
- 'quote_line': fields.one2many('sale.quote.line', 'quote_id', 'Quote Template Lines'),
+ 'quote_line': fields.one2many('sale.quote.line', 'quote_id', 'Quote Template Lines', copy=True),
'note': fields.text('Terms and conditions'),
- 'options': fields.one2many('sale.quote.option', 'template_id', 'Optional Products Lines'),
+ 'options': fields.one2many('sale.quote.option', 'template_id', 'Optional Products Lines', copy=True),
'number_of_days': fields.integer('Quote Duration', help='Number of days for the validaty date computation of the quotation'),
}
def open_template(self, cr, uid, quote_id, context=None):
@@ -110,7 +110,7 @@ class sale_order(osv.osv):
return res
_columns = {
- 'access_token': fields.char('Security Token', required=True),
+ 'access_token': fields.char('Security Token', required=True, copy=False),
'template_id': fields.many2one('sale.quote.template', 'Quote Template'),
'website_description': fields.html('Description'),
'options' : fields.one2many('sale.order.option', 'order_id', 'Optional Products Lines'),
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index e56e3bb1478..2c52e21e281 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -2,8 +2,8 @@
import werkzeug
from openerp import SUPERUSER_ID
-from openerp.addons.web import http
-from openerp.addons.web.http import request
+from openerp import http
+from openerp.http import request
from openerp.tools.translate import _
from openerp.addons.website.models.website import slug
@@ -582,7 +582,7 @@ class website_sale(http.Controller):
# acquirer_ids = [tx.acquirer_id.id]
# else:
acquirer_ids = payment_obj.search(cr, SUPERUSER_ID, [('website_published', '=', True)], context=context)
- values['acquirers'] = payment_obj.browse(cr, uid, acquirer_ids, context=context)
+ values['acquirers'] = list(payment_obj.browse(cr, uid, acquirer_ids, context=context))
render_ctx = dict(context, submit_class='btn btn-primary', submit_txt='Pay Now')
for acquirer in values['acquirers']:
acquirer.button = payment_obj.render(
diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py
index ce206372047..29e1fca7f57 100644
--- a/addons/website_sale/models/product.py
+++ b/addons/website_sale/models/product.py
@@ -122,7 +122,7 @@ class product_template(osv.Model):
],
string='Website Comments',
),
- 'website_published': fields.boolean('Available in the website'),
+ 'website_published': fields.boolean('Available in the website', copy=False),
'website_description': fields.html('Description for the website'),
'alternative_product_ids': fields.many2many('product.template','product_alternative_rel','src_id','dest_id', string='Alternative Products', help='Appear on the product page'),
'accessory_product_ids': fields.many2many('product.product','product_accessory_rel','src_id','dest_id', string='Accessory Products', help='Appear on the shopping cart'),
diff --git a/addons/website_sale_delivery/models/sale_order.py b/addons/website_sale_delivery/models/sale_order.py
index 029190d35c8..ebea78803fd 100644
--- a/addons/website_sale_delivery/models/sale_order.py
+++ b/addons/website_sale_delivery/models/sale_order.py
@@ -8,7 +8,7 @@ from openerp.addons import decimal_precision
class delivery_carrier(orm.Model):
_inherit = 'delivery.carrier'
_columns = {
- 'website_published': fields.boolean('Available in the website'),
+ 'website_published': fields.boolean('Available in the website', copy=False),
'website_description': fields.text('Description for the website'),
}
_defaults = {
diff --git a/doc/03_module_dev_02.rst b/doc/03_module_dev_02.rst
index 223c82d3f1c..ec7fa57d0dc 100644
--- a/doc/03_module_dev_02.rst
+++ b/doc/03_module_dev_02.rst
@@ -615,6 +615,7 @@ Relational Types
reference. :guilabel:`relation` is the table to look up that
reference in.
+.. _fields-functional:
Functional Fields
+++++++++++++++++
diff --git a/doc/03_module_dev_03.rst b/doc/03_module_dev_03.rst
index e5e309fe00e..74fb4772f84 100644
--- a/doc/03_module_dev_03.rst
+++ b/doc/03_module_dev_03.rst
@@ -70,15 +70,21 @@ As we can see below in the purple zone of the screen, there is also a way to dis
On Change
+++++++++
-The on_change attribute defines a method that is called when the content of a view field has changed.
+The on_change attribute defines a method that is called when the
+content of a view field has changed.
-This method takes at least arguments: cr, uid, ids, which are the three classical arguments and also the context dictionary. You can add parameters to the method. They must correspond to other fields defined in the view, and must also be defined in the XML with fields defined this way::
+This method takes at least arguments: cr, uid, ids, which are the
+three classical arguments and also the context dictionary. You can add
+parameters to the method. They must correspond to other fields defined
+in the view, and must also be defined in the XML with fields defined
+this way::
The example below is from the sale order view.
-You can use the 'context' keyword to access data in the context that can be used as params of the function.::
+You can use the 'context' keyword to access data in the context that
+can be used as params of the function.::
@@ -100,7 +106,10 @@ You can use the 'context' keyword to access data in the context that can be used
return {'value':v}
-When editing the shop_id form field, the onchange_shop_id method of the sale_order object is called and returns a dictionary where the 'value' key contains a dictionary of the new value to use in the 'project_id', 'pricelist_id' and 'payment_default_id' fields.
+When editing the shop_id form field, the onchange_shop_id method of
+the sale_order object is called and returns a dictionary where the
+'value' key contains a dictionary of the new value to use in the
+'project_id', 'pricelist_id' and 'payment_default_id' fields.
Note that it is possible to change more than just the values of
fields. For example, it is possible to change the value of some fields
diff --git a/doc/api_models.rst b/doc/api_models.rst
index 143147c0f48..82f45ed5c9c 100644
--- a/doc/api_models.rst
+++ b/doc/api_models.rst
@@ -1,7 +1,21 @@
-ORM and models
---------------
+ORM and Models
+==============
.. automodule:: openerp.osv.orm
:members:
:undoc-members:
+
+Scope Management
+================
+
+.. automodule:: openerp.osv.scope
+ :members:
+ :undoc-members:
+
+API Decorators
+==============
+
+.. automodule:: openerp.osv.api
+ :members:
+ :undoc-members:
diff --git a/doc/howto/howto_website.rst b/doc/howto/howto_website.rst
index e4ef9b4748b..722891519da 100644
--- a/doc/howto/howto_website.rst
+++ b/doc/howto/howto_website.rst
@@ -65,7 +65,7 @@ module in it:
.. code-block:: console
$ createdb academy
- $ ./openerp-server --addons-path=../web/addons,../addons,../my-modules \
+ $ ./openerp-server --addons-path=addons,../my-modules \
-d academy -i academy --db-filter=academy
* ``--addons-path`` tells OpenERP where it can find its modules. By default it
@@ -159,6 +159,10 @@ Let's move our 2 pseudo-templates from inline strings to actual templates:
This simplifies the controller code by moving data formatting out of it, and
generally makes it simpler for designers to edit the markup.
+.. note::
+
+ You'll need to update the module to install the new templates
+
.. todo:: link to section about reusing/altering existing stuff, template
overriding
@@ -175,7 +179,7 @@ First, we'll install the ``website`` module: restart your server with
.. code-block:: console
- $ ./openerp-server --addons-path=../web/addons,../addons,../my-modules \
+ $ ./openerp-server --addons-path=addons,../my-modules \
-d academy -i website --db-filter=academy
If you navigate to `your openerp`_, your basic page may have been replaced by
@@ -202,7 +206,7 @@ ensures ``academy``'s index page overwrites ``website``'s.
.. code-block:: console
- $ ./openerp-server --addons-path=../web/addons,../addons,../my-modules \
+ $ ./openerp-server --addons-path=addons,../my-modules \
-d academy -u academy --db-filter=academy
instead of the previous command (note: ``-i`` was replaced by ``-u``)
@@ -460,14 +464,14 @@ of all records in the object) and the "form" view (view an manipulation of a
single record). The :guilabel:`Create` button above the list lets you create
new record, you can select records to delete them.
-There's one big issue to fix right now, the labeling of the column in the list
-and the fields in the form view, which are all currently :guilabel:`unknown`.
-We can fix that by adding a ``string`` attribute to the model field:
+The names of the fields in the search and list view are automatically inferred
+from the logical field names, but it's probably a good idea to specify them
+anyway, by adding a ``string`` to the model field:
.. patch::
-The second problem is that the list view only displays the ``name`` field. To
-fix this, we have to create an explicit list view for lectures:
+An issue is that the list view only displays the ``name`` field. To fix this,
+we have to create an explicit list view for lectures:
.. patch::
@@ -491,7 +495,7 @@ the server as:
.. code-block:: console
- $ ./openerp-server --addons-path=../web/addons,../addons,../my-modules \
+ $ ./openerp-server --addons-path=addons,../my-modules \
-d academy -i website_event --db-filter=academy
We'll also add it as a dependency to our module:
@@ -517,7 +521,7 @@ Restart the server with
.. code-block:: console
- $ ./openerp-server --addons-path=../web/addons,../addons,../my-modules \
+ $ ./openerp-server --addons-path=addons,../my-modules \
-d academy -i academy --db-filter=academy
and the menu item has been renamed to Lectures.
@@ -573,6 +577,12 @@ The gist of the operation is fairly simple, but there are lots of changes:
purged if we do not need it (e.g. existing non-lectures events and event
types can be removed before adding our own)
+.. note::
+
+ because we're reusing the old XIDs on completely different models, we need
+ to either remove the old reference or (simpler) just drop and re-create
+ the database
+
.. patch::
Our data is back in the fontend (site), and in the backend we get
diff --git a/doc/howto/howto_website/basic-page b/doc/howto/howto_website/basic-page
index d3179145542..ccb47652f39 100644
--- a/doc/howto/howto_website/basic-page
+++ b/doc/howto/howto_website/basic-page
@@ -1,5 +1,5 @@
# HG changeset patch
-# Parent b96cd22d25cfa9a67f451d091f5c4896997d350d
+# Parent 11a30af5c0c80b15a2bbad562ef3a580ee99fb3b
diff --git a/controllers/academy.py b/controllers/academy.py
--- a/controllers/academy.py
@@ -10,7 +10,7 @@ diff --git a/controllers/academy.py b/controllers/academy.py
def index(self):
- return "Hello, world!"
+ return """
-+
++
+
+ Hello, world!
+
diff --git a/doc/howto/howto_website/field-label b/doc/howto/howto_website/field-label
index 2589e8f872a..090344dc719 100644
--- a/doc/howto/howto_website/field-label
+++ b/doc/howto/howto_website/field-label
@@ -1,14 +1,14 @@
# HG changeset patch
-# Parent fe4edbcd9e98db81ec6321c58e8ac508a686f45b
-diff -r fe4edbcd9e98 -r 72a099819e5b models/academy.py
---- a/models/academy.py Mon Apr 14 16:38:10 2014 +0200
-+++ b/models/academy.py Mon Apr 14 16:59:01 2014 +0200
-@@ -14,6 +14,6 @@ class Lectures(orm.Model):
+# Parent 4b38aba926d27af5f81be1a3b5a482c26522bf38
+
+diff --git a/models/academy.py b/models/academy.py
+--- a/models/academy.py
++++ b/models/academy.py
+@@ -12,5 +12,5 @@ class Lectures(Model):
+ _name = 'academy.lectures'
_order = 'date ASC'
- _columns = {
-- 'name': fields.char(required=True),
-- 'date': fields.date(required=True),
-+ 'name': fields.char(required=True, string="Name"),
-+ 'date': fields.date(required=True, string="Date"),
- }
+- name = Char(required=True)
+- date = Date(required=True)
++ name = Char(required=True, string="Name")
++ date = Date(required=True, string="Date")
diff --git a/doc/howto/howto_website/lectures-model-add b/doc/howto/howto_website/lectures-model-add
index e622ec6656f..3211995ab72 100644
--- a/doc/howto/howto_website/lectures-model-add
+++ b/doc/howto/howto_website/lectures-model-add
@@ -1,5 +1,5 @@
# HG changeset patch
-# Parent cd98e5752eedca2780da80387ac01c8cd166940f
+# Parent 13c65b2eed8f84b951ff5a98051f64fe35ddcf0a
diff --git a/__openerp__.py b/__openerp__.py
--- a/__openerp__.py
@@ -15,22 +15,17 @@ diff --git a/__openerp__.py b/__openerp__.py
diff --git a/controllers/academy.py b/controllers/academy.py
--- a/controllers/academy.py
+++ b/controllers/academy.py
-@@ -6,10 +6,15 @@ from openerp.addons.web.controllers impo
+@@ -6,8 +6,10 @@ from openerp.addons.web.controllers impo
class academy(main.Home):
@http.route('/', auth='public', website=True)
def index(self):
-+ cr, uid, context = http.request.cr, http.request.uid, http.request.context
-+ Lectures = http.request.registry['academy.lectures']
- tas = http.request.registry['academy.tas'].search_read(
- http.request.cr, http.request.uid, context=http.request.context)
-+ lectures = Lectures.browse(
-+ cr, uid, Lectures.search(cr, uid, [], context=context), context=context)
- return http.request.website.render('academy.index', {
- 'tas': tas,
++ lectures = http.request.env['academy.lectures'].search([])
+ tas = http.request.env['academy.tas'].search([])
+ return http.request.render('academy.index', {
+ 'lectures': lectures,
+ 'tas': tas,
})
- @http.route('/tas//', auth='public', website=True)
diff --git a/data/lectures.xml b/data/lectures.xml
new file mode 100644
--- /dev/null
@@ -63,19 +58,24 @@ new file mode 100644
diff --git a/models/academy.py b/models/academy.py
--- a/models/academy.py
+++ b/models/academy.py
-@@ -8,3 +8,12 @@ class TeachingAssistants(orm.Model):
- 'name': fields.char(),
- 'biography': fields.html(),
- }
+@@ -1,9 +1,16 @@
+ # -*- coding: utf-8 -*-
+ from openerp.models import Model
+-from openerp.fields import Char, Html
++from openerp.fields import Char, Html, Date
+
+ class TeachingAssistants(Model):
+ _name = "academy.tas"
+
+ name = Char()
+ biography = Html()
+
-+class Lectures(orm.Model):
++class Lectures(Model):
+ _name = 'academy.lectures'
+ _order = 'date ASC'
+
-+ _columns = {
-+ 'name': fields.char(required=True),
-+ 'date': fields.date(required=True),
-+ }
++ name = Char(required=True)
++ date = Date(required=True)
diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
--- a/security/ir.model.access.csv
+++ b/security/ir.model.access.csv
diff --git a/doc/howto/howto_website/manifest b/doc/howto/howto_website/manifest
index 601c18c5106..c0c7ee29547 100644
--- a/doc/howto/howto_website/manifest
+++ b/doc/howto/howto_website/manifest
@@ -71,16 +71,15 @@ diff --git a/models/academy.py b/models/academy.py
new file mode 100644
--- /dev/null
+++ b/models/academy.py
-@@ -0,0 +1,9 @@
+@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
-+from openerp.osv import orm, fields
++from openerp.models import Model
++from openerp.fields import Char
+
-+class academy(orm.Model):
++class academy(Model):
+ _name = "academy.academy"
+
-+ _columns = {
-+ 'name': fields.char(),
-+ }
++ name = Char()
diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
new file mode 100644
--- /dev/null
diff --git a/doc/howto/howto_website/move-to-openerp-objects b/doc/howto/howto_website/move-to-openerp-objects
index 8623d8a90e5..0efebb39846 100644
--- a/doc/howto/howto_website/move-to-openerp-objects
+++ b/doc/howto/howto_website/move-to-openerp-objects
@@ -1,5 +1,5 @@
# HG changeset patch
-# Parent ade038cd6dfd855f1d423cffb3d4e242404c28f0
+# Parent bc8914ae6b552520bdc3856e81d3624dc5a77a99
diff --git a/__init__.py b/__init__.py
--- a/__init__.py
@@ -26,40 +26,31 @@ diff --git a/__openerp__.py b/__openerp__.py
diff --git a/controllers/academy.py b/controllers/academy.py
--- a/controllers/academy.py
+++ b/controllers/academy.py
-@@ -6,18 +6,29 @@ from openerp.addons.web.controllers impo
+@@ -6,14 +6,22 @@ from openerp.addons.web.controllers impo
class academy(main.Home):
@http.route('/', auth='public', website=True)
def index(self):
-+ registry = http.request.registry
- cr, uid, context = http.request.cr, http.request.uid, http.request.context
-- Lectures = http.request.registry['academy.lectures']
-- tas = http.request.registry['academy.tas'].search_read(
-- http.request.cr, http.request.uid, context=http.request.context)
+- lectures = http.request.env['academy.lectures'].search([])
+- tas = http.request.env['academy.tas'].search([])
++ ta_group = http.request.env.ref('academy.tas')
++ tas = http.request.env['res.users'].search(
++ [('groups_id', '=', [ta_group.id])]
++ )
+
-+ Data = registry['ir.model.data']
-+ _, ta_group_id = Data.get_object_reference(cr, uid, 'academy', 'tas')
-+ tas = registry['res.users'].search_read(
-+ http.request.cr, http.request.uid,
-+ [('groups_id', '=', [ta_group_id])],
-+ context=http.request.context)
++ lecture_type = http.request.env.ref('academy.lecture_type')
++ lectures = http.request.env['event.event'].search(
++ [('type', '=', lecture_type.id)]
++ )
+
-+ Lectures = registry['event.event']
-+ _, lecture_type_id = Data.get_object_reference(cr, uid, 'academy', 'lecture_type')
- lectures = Lectures.browse(
-- cr, uid, Lectures.search(cr, uid, [], context=context), context=context)
-+ cr, uid,
-+ Lectures.search(cr, uid, [('type', '=', lecture_type_id),], context=context),
-+ context=context)
-+
- return http.request.website.render('academy.index', {
- 'tas': tas,
+ return http.request.render('academy.index', {
'lectures': lectures,
+ 'tas': tas,
})
- @http.route('/tas//', auth='public', website=True)
+ @http.route('/tas//', auth='public', website=True)
def ta(self, ta):
- return http.request.website.render('academy.ta', {
+ return http.request.render('academy.ta', {
'ta': ta,
diff --git a/data/views.xml b/data/views.xml
deleted file mode 100644
@@ -227,39 +218,36 @@ diff --git a/models/academy.py b/models/academy.py
deleted file mode 100644
--- a/models/academy.py
+++ /dev/null
-@@ -1,19 +0,0 @@
+@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
--from openerp.osv import orm, fields
+-from openerp.models import Model
+-from openerp.fields import Char, Html, Date
-
--class TeachingAssistants(orm.Model):
+-class TeachingAssistants(Model):
- _name = "academy.tas"
-
-- _columns = {
-- 'name': fields.char(),
-- 'biography': fields.html(),
-- }
+- name = Char()
+- biography = Html()
-
--class Lectures(orm.Model):
+-class Lectures(Model):
- _name = 'academy.lectures'
- _order = 'date ASC'
-
-- _columns = {
-- 'name': fields.char(required=True, string="Name"),
-- 'date': fields.date(required=True, string="Date"),
-- }
+- name = Char(required=True, string="Name")
+- date = Date(required=True, string="Date")
diff --git a/models/res_partner.py b/models/res_partner.py
new file mode 100644
--- /dev/null
+++ b/models/res_partner.py
@@ -0,0 +1,8 @@
-+from openerp.osv import orm, fields
++from openerp.models import Model
++from openerp.fields import Html
+
-+class Partner(orm.Model):
++class Partner(Model):
+ _inherit = 'res.partner'
+
-+ _columns = {
-+ 'biography': fields.html(),
-+ }
++ biography = Html()
++
diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
deleted file mode 100644
--- a/security/ir.model.access.csv
diff --git a/doc/howto/howto_website/ta-controller b/doc/howto/howto_website/ta-controller
index 64de1dae17b..da4ee7606c9 100644
--- a/doc/howto/howto_website/ta-controller
+++ b/doc/howto/howto_website/ta-controller
@@ -1,5 +1,5 @@
# HG changeset patch
-# Parent a110c540b0769ee849a404324cf8594d116cc982
+# Parent a813df5b9cbdf5db9b0c3f6bac47b1821ddbb086
diff --git a/controllers/academy.py b/controllers/academy.py
--- a/controllers/academy.py
@@ -25,7 +25,7 @@ diff --git a/controllers/academy.py b/controllers/academy.py
+ ]
+
return """
-
+
- Hello, world!
+ AcademyAcademy TA %(name)s
-+
++
+
+
+
Introduction to something
@@ -45,7 +45,7 @@ diff --git a/controllers/academy.py b/controllers/academy.py + + +%(name)s
diff --git a/doc/howto/howto_website/ta-data b/doc/howto/howto_website/ta-data index 395b5a2b430..2c001de47ac 100644 --- a/doc/howto/howto_website/ta-data +++ b/doc/howto/howto_website/ta-data @@ -1,5 +1,5 @@ # HG changeset patch -# Parent a35b5f4903087b1b31a4ecf33bfe655dc3ad5663 +# Parent 650d20edf05a23b977c3b9fa476a39986eee4722 diff --git a/__openerp__.py b/__openerp__.py --- a/__openerp__.py diff --git a/doc/howto/howto_website/ta-html-biography b/doc/howto/howto_website/ta-html-biography index a8460c3090a..c0fae24aa41 100644 --- a/doc/howto/howto_website/ta-html-biography +++ b/doc/howto/howto_website/ta-html-biography @@ -1,12 +1,17 @@ # HG changeset patch -# Parent 313d7c760558b24461a544063de061d00fdf7059 +# Parent d57af6d92026e45b336308b143a5c227d44de9f7 diff --git a/models/academy.py b/models/academy.py --- a/models/academy.py +++ b/models/academy.py -@@ -6,4 +6,5 @@ class TeachingAssistants(orm.Model): +@@ -1,8 +1,9 @@ + # -*- coding: utf-8 -*- + from openerp.models import Model +-from openerp.fields import Char ++from openerp.fields import Char, Html - _columns = { - 'name': fields.char(), -+ 'biography': fields.html(), - } + class TeachingAssistants(Model): + _name = "academy.tas" + + name = Char() ++ biography = Html() diff --git a/doc/howto/howto_website/ta-model b/doc/howto/howto_website/ta-model index d3b79299e9c..277ceea3cf9 100644 --- a/doc/howto/howto_website/ta-model +++ b/doc/howto/howto_website/ta-model @@ -1,10 +1,10 @@ # HG changeset patch -# Parent e8b98f4c8f9070f7d5b91936381324cd1fe12e17 +# Parent 3fbc9c92bcee395900c441aeb80e68036004a7a0 diff --git a/controllers/academy.py b/controllers/academy.py --- a/controllers/academy.py +++ b/controllers/academy.py -@@ -3,19 +3,13 @@ +@@ -3,19 +3,12 @@ from openerp import http from openerp.addons.web.controllers import main @@ -19,9 +19,8 @@ diff --git a/controllers/academy.py b/controllers/academy.py class academy(main.Home): @http.route('/', auth='public', website=True) def index(self): -+ tas = http.request.registry['academy.tas'].search_read( -+ http.request.cr, http.request.uid, context=http.request.context) - return http.request.website.render('academy.index', { ++ tas = http.request.env['academy.tas'].search([]) + return http.request.render('academy.index', { - 'tas': teaching_assistants, + 'tas': tas, }) @@ -30,17 +29,16 @@ diff --git a/controllers/academy.py b/controllers/academy.py diff --git a/models/academy.py b/models/academy.py --- a/models/academy.py +++ b/models/academy.py -@@ -1,8 +1,8 @@ - # -*- coding: utf-8 -*- - from openerp.osv import orm, fields +@@ -2,7 +2,7 @@ + from openerp.models import Model + from openerp.fields import Char --class academy(orm.Model): +-class academy(Model): - _name = "academy.academy" -+class TeachingAssistants(orm.Model): ++class TeachingAssistants(Model): + _name = "academy.tas" - _columns = { - 'name': fields.char(), + name = Char() diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -48,3 +46,15 @@ diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_academy_academy,access_academy_academy,model_academy_academy,,1,0,0,0 +access_academy_tas,access_academy_tas,model_academy_tas,,1,0,0,0 +diff --git a/views/templates.xml b/views/templates.xml +--- a/views/templates.xml ++++ b/views/templates.xml +@@ -17,7 +17,7 @@ +Teaching Assistants
++-
+-
++
+
+
+
diff --git a/doc/howto/howto_website/ta-t-field b/doc/howto/howto_website/ta-t-field
index a61bec85a6b..8d6ffe06d90 100644
--- a/doc/howto/howto_website/ta-t-field
+++ b/doc/howto/howto_website/ta-t-field
@@ -1,9 +1,18 @@
# HG changeset patch
-# Parent 9d054be011cf2d3019e3b4d1be7cc95375044a05
+# Parent a1788b0db87f7fdb5640b17bbda6e9a0c6093c37
diff --git a/views/templates.xml b/views/templates.xml
--- a/views/templates.xml
+++ b/views/templates.xml
+@@ -18,7 +18,7 @@
+
diff --git a/doc/howto/howto_website/ta-view-fix b/doc/howto/howto_website/ta-view-fix
index 5e3f494cf3d..c04c5dc1349 100644
--- a/doc/howto/howto_website/ta-view-fix
+++ b/doc/howto/howto_website/ta-view-fix
@@ -1,5 +1,5 @@
# HG changeset patch
-# Parent 466d19929560c2a60c347990ea44730ae40ec36d
+# Parent 0d88c003a54e6501eee5f43b90db20d9b78c07c8
diff --git a/controllers/academy.py b/controllers/academy.py
--- a/controllers/academy.py
@@ -10,24 +10,15 @@ diff --git a/controllers/academy.py b/controllers/academy.py
- @http.route('/tas//', auth='public', website=True)
- def ta(self, id):
-- return http.request.website.render('academy.ta', teaching_assistants[id])
+- return http.request.render('academy.ta', teaching_assistants[id])
+ @http.route('/tas//', auth='public', website=True)
+ def ta(self, ta):
-+ return http.request.website.render('academy.ta', {
++ return http.request.render('academy.ta', {
+ 'ta': ta,
+ })
diff --git a/views/templates.xml b/views/templates.xml
--- a/views/templates.xml
+++ b/views/templates.xml
-@@ -17,7 +17,7 @@
-
+-
+
+-
++
+
+
+
@@ -35,7 +35,7 @@Teaching Assistants
---
--
-+
-
-
-
@@ -35,7 +35,7 @@
diff --git a/doc/howto/howto_website/templates-basic b/doc/howto/howto_website/templates-basic
index a5b14a3fbc1..95d7614b496 100644
--- a/doc/howto/howto_website/templates-basic
+++ b/doc/howto/howto_website/templates-basic
@@ -1,5 +1,5 @@
# HG changeset patch
-# Parent 2edbac8244a982055a6786c14ee41719ea5410e8
+# Parent 9979cfa9319edcd15e6c243862eeeb03b851a82a
diff --git a/__openerp__.py b/__openerp__.py
--- a/__openerp__.py
@@ -19,18 +19,13 @@ diff --git a/__openerp__.py b/__openerp__.py
diff --git a/controllers/academy.py b/controllers/academy.py
--- a/controllers/academy.py
+++ b/controllers/academy.py
-@@ -14,34 +14,17 @@ teaching_assistants = [
- class academy(main.Home):
- @http.route('/', auth='none')
- def index(self):
-+ cr, uid, context = http.request.cr, http.request.uid, http.request.context
- tas = [
+@@ -18,30 +18,10 @@ class academy(main.Home):
'- %s
' % (i, ta['name'])
for i, ta in enumerate(teaching_assistants)
]
-
+-
- return """
--
+-
-
- /', auth='none')
def ta(self, id):
- return """
-
-
- AcademyAcademy TA %(name)s
--
+-
-
-
- AcademyAcademy
-+
++
+
+
+ AcademyAcademy TA
-+
++
+
+
+ - %s
' % (i, ta['name'])
- for i, ta in enumerate(teaching_assistants)
- ]
-+ return http.request.website.render('academy.index', {
-+ 'tas': teaching_assistants,
-+ })
-
-- return http.request.registry['ir.ui.view'].render(cr, uid, 'academy.index', {
+ return http.request.render('academy.index', {
- 'tas': '\n'.join(tas)
-- }, context=context)
-- @http.route('/tas//', auth='none')
-+ @http.route('/tas//', auth='none', website=True)
- def ta(self, id):
-- cr, uid, context = http.request.cr, http.request.uid, http.request.context
-- return http.request.registry['ir.ui.view'].render(
-- cr, uid, 'academy.ta', teaching_assistants[id], context=context)
-+ return http.request.website.render('academy.ta', teaching_assistants[id])
++ 'tas': teaching_assistants,
+ })
+
+ @http.route('/tas//', auth='none')
diff --git a/views/templates.xml b/views/templates.xml
--- a/views/templates.xml
+++ b/views/templates.xml
diff --git a/doc/howto/howto_website/website-layoutify b/doc/howto/howto_website/website-layoutify
index d4ed9647dd5..a2ae505673c 100644
--- a/doc/howto/howto_website/website-layoutify
+++ b/doc/howto/howto_website/website-layoutify
@@ -1,5 +1,5 @@
# HG changeset patch
-# Parent 69c500d7634c0e5287508cfaffa14174cc47d800
+# Parent 22cbd107041dde59f904e94ea8c740fca5965fc0
diff --git a/controllers/academy.py b/controllers/academy.py
--- a/controllers/academy.py
@@ -8,17 +8,17 @@ diff --git a/controllers/academy.py b/controllers/academy.py
]
class academy(main.Home):
-- @http.route('/', auth='none', website=True)
+- @http.route('/', auth='none')
+ @http.route('/', auth='public', website=True)
def index(self):
- return http.request.website.render('academy.index', {
+ return http.request.render('academy.index', {
'tas': teaching_assistants,
})
-- @http.route('/tas//', auth='none', website=True)
+- @http.route('/tas//', auth='none')
+ @http.route('/tas//', auth='public', website=True)
def ta(self, id):
- return http.request.website.render('academy.ta', teaching_assistants[id])
+ return http.request.render('academy.ta', teaching_assistants[id])
diff --git a/views/templates.xml b/views/templates.xml
--- a/views/templates.xml
+++ b/views/templates.xml
@@ -29,7 +29,7 @@ diff --git a/views/templates.xml b/views/templates.xml
-
-
- AcademyAcademy
--
+-
-
-
- AcademyAcademy TA
--
+-
-
-
- `: a ``multi`` method is
+simply one which computes several values at once::
+
+ @api.depends('company_id')
+ def compute_relations(self):
+ self.computed_company = self.company_id
+ self.computed_companies = self.company_id.to_recordset()
+
+Automatic onchange
+==================
+
+Using to the improved and expanded :ref:`computed fields `, the
+high-level ORM API is able to infer the effect of fields on
+one another, and thus automatically provide a basic form of onchange without
+having to implement it by hand, or implement dozens of onchange functions to
+get everything right.
+
+
+
+
+.. todo::
+
+ deferred records::
+
+ partner = Partner.record(42, defer=True)
+ partner.name = "foo"
+ partner.user_id = juan
+ partner.save() # only saved to db here
+
+ with scope.defer():
+ # all records in this scope or children scopes are deferred
+ # until corresponding scope poped or until *this* scope poped?
+ partner = Partner.record(42)
+ partner.name = "foo"
+ partner.user_id = juan
+ # saved here, also for recordset &al, ~transaction
+
+ # temp deferment, maybe simpler? Or for bulk operations?:
+ with Partner.record(42) as partner:
+ partner.name = "foo"
+ partner.user_id = juan
+
+ ``id = False`` => always defered? null v draft?
+
+.. todo:: keyword arguments passed positionally (common for context, completely breaks everything)
+
+.. todo:: optional arguments (report_aged_receivable)
+
+.. todo:: non-id ids? (mail thread_id)
+
+.. todo:: partial signatures on overrides (e.g. message_post)
+
+.. todo::
+
+ ::
+
+ field = fields.Char()
+
+ @field.computer
+ def foo(self):
+ "compute foo here"
+
+ ~
+
+ ::
+
+ field = fields.Char(compute='foo')
+
+ def foo(self):
+ "compute foo here"
+
+.. todo:: doc
+
+.. todo:: incorrect dependency spec?
+
+.. todo:: dynamic dependencies?
+
+ ::
+
+ @api.depends(???)
+ def foo(self)
+ self.a = self[self.b]
+
+.. todo:: recursive onchange
+
+ Country & state. Change country -> remove state; set state -> set country
+
+.. todo:: onchange list affected?
diff --git a/openerp/__init__.py b/openerp/__init__.py
index 995822c9cb0..24a193bf759 100644
--- a/openerp/__init__.py
+++ b/openerp/__init__.py
@@ -67,9 +67,7 @@ def registry(database_name):
# Imports
#----------------------------------------------------------
import addons
-import cli
import conf
-import http
import loglevels
import modules
import netsvc
@@ -82,5 +80,18 @@ import sql_db
import tools
import workflow
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+#----------------------------------------------------------
+# Model classes, fields, api decorators, and translations
+#----------------------------------------------------------
+from . import models
+from . import fields
+from . import api
+from openerp.tools.translate import _
+#----------------------------------------------------------
+# Other imports, which may require stuff from above
+#----------------------------------------------------------
+import cli
+import http
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/__openerp__.py b/openerp/addons/base/__openerp__.py
index 6db84cb84c4..4bbe2ed5879 100644
--- a/openerp/addons/base/__openerp__.py
+++ b/openerp/addons/base/__openerp__.py
@@ -39,7 +39,6 @@ The kernel of OpenERP, needed for all installation.
'res/res_country_data.xml',
'security/base_security.xml',
'base_menu.xml',
- 'res/res_security.xml',
'res/res_config.xml',
'res/res.country.state.csv',
'ir/ir_actions.xml',
@@ -82,7 +81,7 @@ The kernel of OpenERP, needed for all installation.
'res/res_users_view.xml',
'res/res_partner_data.xml',
'res/ir_property_view.xml',
- 'security/base_security.xml',
+ 'res/res_security.xml',
'security/ir.model.access.csv',
],
'demo': [
diff --git a/openerp/addons/base/base_menu.xml b/openerp/addons/base/base_menu.xml
index d9cd0c4e8bf..5c15addb56d 100644
--- a/openerp/addons/base/base_menu.xml
+++ b/openerp/addons/base/base_menu.xml
@@ -30,6 +30,10 @@
+
+
+
+
Open Settings Menu
reload
diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py
index 2de2301c5df..7ac8783ddd9 100644
--- a/openerp/addons/base/ir/ir_actions.py
+++ b/openerp/addons/base/ir/ir_actions.py
@@ -330,7 +330,7 @@ class ir_actions_act_window(osv.osv):
}
for res in results:
model = res.get('res_model')
- if model and self.pool.get(model):
+ if model in self.pool:
try:
with tools.mute_logger("openerp.tools.safe_eval"):
eval_context = eval(res['context'] or "{}", eval_dict) or {}
@@ -339,7 +339,7 @@ class ir_actions_act_window(osv.osv):
continue
if not fields or 'help' in fields:
custom_context = dict(context, **eval_context)
- res['help'] = self.pool.get(model).get_empty_list_help(cr, uid, res.get('help', ""), context=custom_context)
+ res['help'] = self.pool[model].get_empty_list_help(cr, uid, res.get('help', ""), context=custom_context)
if ids_int:
return results[0]
return results
@@ -355,7 +355,7 @@ class ir_actions_act_window(osv.osv):
dataobj = self.pool.get('ir.model.data')
data_id = dataobj._get_id (cr, SUPERUSER_ID, module, xml_id)
res_id = dataobj.browse(cr, uid, data_id, context).res_id
- return self.read(cr, uid, res_id, [], context)
+ return self.read(cr, uid, [res_id], [], context)[0]
VIEW_TYPES = [
('tree', 'Tree'),
@@ -550,7 +550,7 @@ class ir_actions_server(osv.osv):
help="Provide an expression that, applied on the current record, gives the field to update."),
'fields_lines': fields.one2many('ir.server.object.lines', 'server_id',
string='Value Mapping',
- help=""),
+ copy=True),
# Fake fields used to implement the placeholder assistant
'model_object_field': fields.many2one('ir.model.fields', string="Field",
@@ -577,7 +577,7 @@ class ir_actions_server(osv.osv):
'sequence': 5,
'code': """# You can use the following variables:
# - self: ORM model of the record on which the action is triggered
-# - object: browse_record of the record on which the action is triggered if there is one, otherwise None
+# - object: Record on which the action is triggered if there is one, otherwise None
# - pool: ORM model pool (i.e. self.pool)
# - cr: database cursor
# - uid: current user id
@@ -838,7 +838,7 @@ class ir_actions_server(osv.osv):
def run_action_client_action(self, cr, uid, action, eval_context=None, context=None):
if not action.action_id:
raise osv.except_osv(_('Error'), _("Please specify an action to launch!"))
- return self.pool[action.action_id.type].read(cr, uid, action.action_id.id, context=context)
+ return self.pool[action.action_id.type].read(cr, uid, [action.action_id.id], context=context)[0]
def run_action_code_multi(self, cr, uid, action, eval_context=None, context=None):
eval(action.code.strip(), eval_context, mode="exec", nocopy=True) # nocopy allows to return 'action'
@@ -1100,10 +1100,10 @@ Launch Manually Once: after having been launched manually, it sets automatically
wizard.write({'state': 'done'})
# Load action
- act_type = self.pool.get('ir.actions.actions').read(cr, uid, wizard.action_id.id, ['type'], context=context)
+ act_type = wizard.action_id.type
- res = self.pool[act_type['type']].read(cr, uid, wizard.action_id.id, [], context=context)
- if act_type['type'] != 'ir.actions.act_window':
+ res = self.pool[act_type].read(cr, uid, [wizard.action_id.id], [], context=context)[0]
+ if act_type != 'ir.actions.act_window':
return res
res.setdefault('context','{}')
res['nodestroy'] = True
diff --git a/openerp/addons/base/ir/ir_attachment.py b/openerp/addons/base/ir/ir_attachment.py
index ef4a6b205a7..ae28a89aa21 100644
--- a/openerp/addons/base/ir/ir_attachment.py
+++ b/openerp/addons/base/ir/ir_attachment.py
@@ -56,12 +56,12 @@ class ir_attachment(osv.osv):
if model_object and res_id:
model_pool = self.pool[model_object]
res = model_pool.name_get(cr,uid,[res_id],context)
- res_name = res and res[0][1] or False
+ res_name = res and res[0][1] or None
if res_name:
field = self._columns.get('res_name',False)
if field and len(res_name) > field.size:
res_name = res_name[:30] + '...'
- data[attachment.id] = res_name
+ data[attachment.id] = res_name or False
else:
data[attachment.id] = False
return data
@@ -273,7 +273,7 @@ class ir_attachment(osv.osv):
# performed in batch as much as possible.
ima = self.pool.get('ir.model.access')
for model, targets in model_attachments.iteritems():
- if not self.pool.get(model):
+ if model not in self.pool:
continue
if not ima.check(cr, uid, model, 'read', False):
# remove all corresponding attachment ids
@@ -297,7 +297,7 @@ class ir_attachment(osv.osv):
if isinstance(ids, (int, long)):
ids = [ids]
self.check(cr, uid, ids, 'read', context=context)
- return super(ir_attachment, self).read(cr, uid, ids, fields_to_read, context, load)
+ return super(ir_attachment, self).read(cr, uid, ids, fields_to_read, context=context, load=load)
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)):
diff --git a/openerp/addons/base/ir/ir_cron.py b/openerp/addons/base/ir/ir_cron.py
index 8350972fa27..60790c8fdc6 100644
--- a/openerp/addons/base/ir/ir_cron.py
+++ b/openerp/addons/base/ir/ir_cron.py
@@ -26,7 +26,7 @@ from datetime import datetime
from dateutil.relativedelta import relativedelta
import openerp
-from openerp import netsvc
+from openerp import SUPERUSER_ID, netsvc, api
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.safe_eval import safe_eval as eval
@@ -149,36 +149,38 @@ class ir_cron(osv.osv):
except Exception, e:
self._handle_callback_exception(cr, uid, model_name, method_name, args, job_id, e)
- def _process_job(self, job_cr, job, cron_cr):
+ def _process_job(self, cr, job, cron_cr):
""" Run a given job taking care of the repetition.
- :param job_cr: cursor to use to execute the job, safe to commit/rollback
+ :param cr: cursor to use to execute the job, safe to commit/rollback
:param job: job to be run (as a dictionary).
:param cron_cr: cursor holding lock on the cron job row, to use to update the next exec date,
must not be committed/rolled back!
"""
try:
- now = datetime.now()
- nextcall = datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT)
- numbercall = job['numbercall']
+ with api.Environment.manage():
+ now = datetime.now()
+ nextcall = datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT)
+ numbercall = job['numbercall']
- ok = False
- while nextcall < now and numbercall:
- if numbercall > 0:
- numbercall -= 1
- if not ok or job['doall']:
- self._callback(job_cr, job['user_id'], job['model'], job['function'], job['args'], job['id'])
- if numbercall:
- nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
- ok = True
- addsql = ''
- if not numbercall:
- addsql = ', active=False'
- cron_cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s",
- (nextcall.strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id']))
+ ok = False
+ while nextcall < now and numbercall:
+ if numbercall > 0:
+ numbercall -= 1
+ if not ok or job['doall']:
+ self._callback(cr, job['user_id'], job['model'], job['function'], job['args'], job['id'])
+ if numbercall:
+ nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
+ ok = True
+ addsql = ''
+ if not numbercall:
+ addsql = ', active=False'
+ cron_cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s",
+ (nextcall.strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id']))
+ self.invalidate_cache(cr, SUPERUSER_ID)
finally:
- job_cr.commit()
+ cr.commit()
cron_cr.commit()
@classmethod
diff --git a/openerp/addons/base/ir/ir_exports.py b/openerp/addons/base/ir/ir_exports.py
index 745ab7cd233..2bdc639024a 100644
--- a/openerp/addons/base/ir/ir_exports.py
+++ b/openerp/addons/base/ir/ir_exports.py
@@ -29,7 +29,7 @@ class ir_exports(osv.osv):
'name': fields.char('Export Name'),
'resource': fields.char('Resource', select=True),
'export_fields': fields.one2many('ir.exports.line', 'export_id',
- 'Export ID'),
+ 'Export ID', copy=True),
}
diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py
index 217ea319d8e..f70d3de33c3 100644
--- a/openerp/addons/base/ir/ir_fields.py
+++ b/openerp/addons/base/ir/ir_fields.py
@@ -31,6 +31,10 @@ LINK_TO = lambda id: (4, id, False)
DELETE_ALL = lambda: (5, False, False)
REPLACE_WITH = lambda ids: (6, False, ids)
+class ImportWarning(Warning):
+ """ Used to send warnings upwards the stack during the import process """
+ pass
+
class ConversionNotFound(ValueError): pass
class ColumnWrapper(object):
@@ -124,7 +128,7 @@ class ir_fields_converter(orm.Model):
If a converter can perform its function but has to make assumptions
about the data, it can send a warning to the user through adding an
- instance of :class:`~openerp.osv.orm.ImportWarning` to the second value
+ instance of :class:`~.ImportWarning` to the second value
it returns. The handling of a warning at the upper levels is the same
as ``ValueError`` above.
@@ -165,7 +169,7 @@ class ir_fields_converter(orm.Model):
))
if value.lower() in falses: return False, []
- return True, [orm.ImportWarning(
+ return True, [ImportWarning(
_(u"Unknown value '%s' for boolean field '%%(field)s', assuming '%s'")
% (value, yes), {
'moreinfo': _(u"Use '1' for yes and '0' for no")
@@ -334,7 +338,7 @@ class ir_fields_converter(orm.Model):
cr, uid, name=value, operator='=', context=context)
if ids:
if len(ids) > 1:
- warnings.append(orm.ImportWarning(
+ warnings.append(ImportWarning(
_(u"Found multiple matches for field '%%(field)s' (%d matches)")
% (len(ids))))
id, _name = ids[0]
diff --git a/openerp/addons/base/ir/ir_http.py b/openerp/addons/base/ir/ir_http.py
index 21c8b7ef941..f2967a02e70 100644
--- a/openerp/addons/base/ir/ir_http.py
+++ b/openerp/addons/base/ir/ir_http.py
@@ -5,9 +5,13 @@ import logging
import re
import sys
-import werkzeug
+import werkzeug.exceptions
+import werkzeug.routing
+import werkzeug.urls
+import werkzeug.utils
import openerp
+import openerp.exceptions
from openerp import http
from openerp.http import request
from openerp.osv import osv, orm
@@ -59,7 +63,7 @@ class ir_http(osv.AbstractModel):
request.uid = request.session.uid
if not request.uid:
if not request.params.get('noredirect'):
- query = werkzeug.url_encode({
+ query = werkzeug.urls.url_encode({
'redirect': request.httprequest.url,
})
response = werkzeug.utils.redirect('/web/login?%s' % query)
diff --git a/openerp/addons/base/ir/ir_mail_server.py b/openerp/addons/base/ir/ir_mail_server.py
index 747f4f1446d..aeded8a1295 100644
--- a/openerp/addons/base/ir/ir_mail_server.py
+++ b/openerp/addons/base/ir/ir_mail_server.py
@@ -19,12 +19,12 @@
#
##############################################################################
-from email.MIMEText import MIMEText
-from email.MIMEBase import MIMEBase
-from email.MIMEMultipart import MIMEMultipart
-from email.Charset import Charset
-from email.Header import Header
-from email.Utils import formatdate, make_msgid, COMMASPACE
+from email.mime.text import MIMEText
+from email.mime.base import MIMEBase
+from email.mime.multipart import MIMEMultipart
+from email.charset import Charset
+from email.header import Header
+from email.utils import formatdate, make_msgid, COMMASPACE
from email import Encoders
import logging
import re
@@ -461,21 +461,18 @@ class ir_mail_server(osv.osv):
mdir.add(message.as_string(True))
return message_id
+ smtp = None
try:
smtp = self.connect(smtp_server, smtp_port, smtp_user, smtp_password, smtp_encryption or False, smtp_debug)
smtp.sendmail(smtp_from, smtp_to_list, message.as_string())
finally:
- try:
- # Close Connection of SMTP Server
+ if smtp is not None:
smtp.quit()
- except Exception:
- # ignored, just a consequence of the previous exception
- pass
except Exception, e:
msg = _("Mail delivery failed via SMTP server '%s'.\n%s: %s") % (tools.ustr(smtp_server),
e.__class__.__name__,
tools.ustr(e))
- _logger.exception(msg)
+ _logger.error(msg)
raise MailDeliveryException(_("Mail Delivery Failed"), msg)
return message_id
diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py
index c759dace9c7..e29b44f0678 100644
--- a/openerp/addons/base/ir/ir_model.py
+++ b/openerp/addons/base/ir/ir_model.py
@@ -28,12 +28,11 @@ import openerp
import openerp.modules.registry
from openerp import SUPERUSER_ID
from openerp import tools
-from openerp.osv import fields,osv
-from openerp.osv.orm import Model, browse_null
-from openerp.tools.safe_eval import safe_eval as eval
+from openerp.osv import fields, osv
+from openerp.osv.orm import BaseModel, Model, MAGIC_COLUMNS, except_orm
from openerp.tools import config
+from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
-from openerp.osv.orm import except_orm, browse_record, MAGIC_COLUMNS
_logger = logging.getLogger(__name__)
@@ -99,7 +98,7 @@ class ir_model(osv.osv):
'name': fields.char('Model Description', translate=True, required=True),
'model': fields.char('Model', required=True, select=1),
'info': fields.text('Information'),
- 'field_id': fields.one2many('ir.model.fields', 'model_id', 'Fields', required=True),
+ 'field_id': fields.one2many('ir.model.fields', 'model_id', 'Fields', required=True, copy=True),
'state': fields.selection([('manual','Custom Object'),('base','Base Object')],'Type', readonly=True),
'access_ids': fields.one2many('ir.model.access', 'model_id', 'Access'),
'osv_memory': fields.function(_is_osv_memory, string='Transient Model', type='boolean',
@@ -133,15 +132,10 @@ class ir_model(osv.osv):
('obj_name_uniq', 'unique (model)', 'Each model must be unique!'),
]
- # overridden to allow searching both on model name (model field)
- # and model description (name field)
- def _name_search(self, cr, uid, name='', args=None, operator='ilike', context=None, limit=100, name_get_uid=None):
- if args is None:
- args = []
- domain = args + ['|', ('model', operator, name), ('name', operator, name)]
- return self.name_get(cr, name_get_uid or uid,
- super(ir_model, self).search(cr, uid, domain, limit=limit, context=context),
- context=context)
+ def _search_display_name(self, operator, value):
+ # overridden to allow searching both on model name (model field) and
+ # model description (name field)
+ return ['|', ('model', operator, value), ('name', operator, value)]
def _drop_table(self, cr, uid, ids, context=None):
for model in self.browse(cr, uid, ids, context):
@@ -177,6 +171,7 @@ class ir_model(osv.osv):
def write(self, cr, user, ids, vals, context=None):
if context:
+ context = dict(context)
context.pop('__last_update', None)
# Filter out operations 4 link from field id, because openerp-web
# always write (4,id,False) even for non dirty items
@@ -207,7 +202,7 @@ class ir_model(osv.osv):
_custom = True
x_custom_model._name = model
x_custom_model._module = False
- a = x_custom_model.create_instance(self.pool, cr)
+ a = x_custom_model._build_model(self.pool, cr)
if not a._columns:
x_name = 'id'
elif 'x_name' in a._columns.keys():
@@ -629,8 +624,8 @@ class ir_model_access(osv.osv):
""" Check if a specific group has the access mode to the specified model"""
assert mode in ['read','write','create','unlink'], 'Invalid access mode'
- if isinstance(model, browse_record):
- assert model._table_name == 'ir.model', 'Invalid model object'
+ if isinstance(model, BaseModel):
+ assert model._name == 'ir.model', 'Invalid model object'
model_name = model.name
else:
model_name = model
@@ -688,8 +683,8 @@ class ir_model_access(osv.osv):
assert mode in ['read','write','create','unlink'], 'Invalid access mode'
- if isinstance(model, browse_record):
- assert model._table_name == 'ir.model', 'Invalid model object'
+ if isinstance(model, BaseModel):
+ assert model._name == 'ir.model', 'Invalid model object'
model_name = model.model
else:
model_name = model
@@ -757,6 +752,7 @@ class ir_model_access(osv.osv):
pass
def call_cache_clearing_methods(self, cr):
+ self.invalidate_cache(cr, SUPERUSER_ID)
self.check.clear_cache(self) # clear the cache of check function
for model, method in self.__cache_clearing_methods:
if model in self.pool:
@@ -765,19 +761,19 @@ class ir_model_access(osv.osv):
#
# Check rights on actions
#
- def write(self, cr, uid, *args, **argv):
+ def write(self, cr, uid, ids, values, context=None):
self.call_cache_clearing_methods(cr)
- res = super(ir_model_access, self).write(cr, uid, *args, **argv)
+ res = super(ir_model_access, self).write(cr, uid, ids, values, context=context)
return res
- def create(self, cr, uid, *args, **argv):
+ def create(self, cr, uid, values, context=None):
self.call_cache_clearing_methods(cr)
- res = super(ir_model_access, self).create(cr, uid, *args, **argv)
+ res = super(ir_model_access, self).create(cr, uid, values, context=context)
return res
- def unlink(self, cr, uid, *args, **argv):
+ def unlink(self, cr, uid, ids, context=None):
self.call_cache_clearing_methods(cr)
- res = super(ir_model_access, self).unlink(cr, uid, *args, **argv)
+ res = super(ir_model_access, self).unlink(cr, uid, ids, context=context)
return res
class ir_model_data(osv.osv):
@@ -833,8 +829,8 @@ class ir_model_data(osv.osv):
'date_init': fields.datetime('Init Date')
}
_defaults = {
- 'date_init': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
- 'date_update': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
+ 'date_init': fields.datetime.now,
+ 'date_update': fields.datetime.now,
'noupdate': False,
'module': ''
}
@@ -844,12 +840,11 @@ class ir_model_data(osv.osv):
def __init__(self, pool, cr):
osv.osv.__init__(self, pool, cr)
- self.doinit = True
# also stored in pool to avoid being discarded along with this osv instance
if getattr(pool, 'model_data_reference_ids', None) is None:
self.pool.model_data_reference_ids = {}
-
- self.loads = self.pool.model_data_reference_ids
+ # put loads on the class, in order to share it among all instances
+ type(self).loads = self.pool.model_data_reference_ids
def _auto_init(self, cr, context=None):
super(ir_model_data, self)._auto_init(cr, context)
@@ -888,7 +883,7 @@ class ir_model_data(osv.osv):
def xmlid_to_object(self, cr, uid, xmlid, raise_if_not_found=False, context=None):
""" Return a browse_record
- if not found and raise_if_not_found is True return the browse_null
+ if not found and raise_if_not_found is True return None
"""
t = self.xmlid_to_res_model_res_id(cr, uid, xmlid, raise_if_not_found)
res_model, res_id = t
@@ -899,7 +894,7 @@ class ir_model_data(osv.osv):
return record
if raise_if_not_found:
raise ValueError('No record found for unique ID %s. It may have been deleted.' % (xml_id))
- return browse_null()
+ return None
# OLD API
def _get_id(self, cr, uid, module, xml_id):
@@ -924,7 +919,7 @@ class ir_model_data(osv.osv):
def get_object(self, cr, uid, module, xml_id, context=None):
""" Returns a browsable record for the given module name and xml_id.
- If not found, raise a ValueError or return a browse_null, depending
+ If not found, raise a ValueError or return None, depending
on the value of `raise_exception`.
"""
return self.xmlid_to_object(cr, uid, "%s.%s" % (module, xml_id), raise_if_not_found=True, context=context)
@@ -961,8 +956,6 @@ class ir_model_data(osv.osv):
if xml_id and ('.' in xml_id):
assert len(xml_id.split('.'))==2, _("'%s' contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id") % xml_id
module, xml_id = xml_id.split('.')
- if (not xml_id) and (not self.doinit):
- return False
action_id = False
if xml_id:
cr.execute('''SELECT imd.id, imd.res_id, md.id, imd.model, imd.noupdate
@@ -1034,8 +1027,8 @@ class ir_model_data(osv.osv):
if xml_id and res_id:
self.loads[(module, xml_id)] = (model, res_id)
for table, inherit_field in model_obj._inherits.iteritems():
- inherit_id = model_obj.read(cr, uid, res_id,
- [inherit_field])[inherit_field]
+ inherit_id = model_obj.read(cr, uid, [res_id],
+ [inherit_field])[0][inherit_field]
self.loads[(module, xml_id + '_' + table.replace('.', '_'))] = (table, inherit_id)
return res_id
@@ -1058,11 +1051,12 @@ class ir_model_data(osv.osv):
cr.execute('select * from ir_values where model=%s and key=%s and name=%s'+where,(model, key, name))
res = cr.fetchone()
+ ir_values_obj = openerp.registry(cr.dbname)['ir.values']
if not res:
- ir_values_obj = openerp.registry(cr.dbname)['ir.values']
ir_values_obj.set(cr, uid, key, key2, name, models, value, replace, isobject, meta)
elif xml_id:
cr.execute('UPDATE ir_values set value=%s WHERE model=%s and key=%s and name=%s'+where,(value, model, key, name))
+ ir_values_obj.invalidate_cache(cr, uid, ['value'])
return True
def _module_data_uninstall(self, cr, uid, modules_to_remove, context=None):
@@ -1104,6 +1098,7 @@ class ir_model_data(osv.osv):
cr.execute('select res_type,res_id from wkf_instance where id IN (select inst_id from wkf_workitem where act_id=%s)', (res_id,))
wkf_todo.extend(cr.fetchall())
cr.execute("update wkf_transition set condition='True', group_id=NULL, signal=NULL,act_to=act_from,act_from=%s where act_to=%s", (res_id,res_id))
+ self.invalidate_cache(cr, uid, context=context)
for model,res_id in wkf_todo:
try:
@@ -1125,7 +1120,7 @@ class ir_model_data(osv.osv):
_logger.info('Deleting orphan external_ids %s', external_ids)
self.unlink(cr, uid, external_ids)
continue
- if field.name in openerp.osv.orm.LOG_ACCESS_COLUMNS and self.pool[field.model]._log_access:
+ if field.name in openerp.models.LOG_ACCESS_COLUMNS and self.pool[field.model]._log_access:
continue
if field.name == 'id':
continue
diff --git a/openerp/addons/base/ir/ir_qweb.py b/openerp/addons/base/ir/ir_qweb.py
index 27822c78246..47866b69eb5 100644
--- a/openerp/addons/base/ir/ir_qweb.py
+++ b/openerp/addons/base/ir/ir_qweb.py
@@ -445,7 +445,7 @@ class QWeb(orm.AbstractModel):
record, field_name = template_attributes["field"].rsplit('.', 1)
record = self.eval_object(record, qwebcontext)
- column = record._model._all_columns[field_name].column
+ column = record._all_columns[field_name].column
options = json.loads(template_attributes.get('field-options') or '{}')
field_type = get_field_type(column, options)
@@ -506,10 +506,10 @@ class FieldConverter(osv.AbstractModel):
:returns: iterable of (attribute name, attribute value) pairs.
"""
- column = record._model._all_columns[field_name].column
+ column = record._all_columns[field_name].column
field_type = get_field_type(column, options)
return [
- ('data-oe-model', record._model._name),
+ ('data-oe-model', record._name),
('data-oe-id', record.id),
('data-oe-field', field_name),
('data-oe-type', field_type),
@@ -541,7 +541,7 @@ class FieldConverter(osv.AbstractModel):
try:
content = self.record_to_html(
cr, uid, field_name, record,
- record._model._all_columns[field_name].column,
+ record._all_columns[field_name].column,
options, context=context)
if options.get('html-escape', True):
content = escape(content)
@@ -549,7 +549,7 @@ class FieldConverter(osv.AbstractModel):
content = content.__html__()
except Exception:
_logger.warning("Could not get field %s for model %s",
- field_name, record._model._name, exc_info=True)
+ field_name, record._name, exc_info=True)
content = None
if context and context.get('inherit_branding'):
@@ -879,7 +879,7 @@ class Contact(orm.AbstractModel):
id = getattr(record, field_name).id
field_browse = self.pool[column._obj].browse(cr, openerp.SUPERUSER_ID, id, context={"show_address": True})
- value = field_browse.name_get()[0][1]
+ value = field_browse.display_name
val = {
'name': value.split("\n")[0],
@@ -888,7 +888,7 @@ class Contact(orm.AbstractModel):
'mobile': field_browse.mobile,
'fax': field_browse.fax,
'city': field_browse.city,
- 'country_id': field_browse.country_id and field_browse.country_id.name_get()[0][1],
+ 'country_id': field_browse.country_id.display_name,
'website': field_browse.website,
'email': field_browse.email,
'fields': opf,
diff --git a/openerp/addons/base/ir/ir_qweb.xml b/openerp/addons/base/ir/ir_qweb.xml
index c69dc17c851..30b366d307c 100644
--- a/openerp/addons/base/ir/ir_qweb.xml
+++ b/openerp/addons/base/ir/ir_qweb.xml
@@ -13,7 +13,9 @@
- ,
+
+ ,
+
diff --git a/openerp/addons/base/ir/ir_rule.py b/openerp/addons/base/ir/ir_rule.py
index 2ac5893038b..c55ad1d5dcd 100644
--- a/openerp/addons/base/ir/ir_rule.py
+++ b/openerp/addons/base/ir/ir_rule.py
@@ -78,7 +78,7 @@ class ir_rule(osv.osv):
'global': fields.function(_get_value, string='Global', type='boolean', store=True, help="If no group is specified the rule is global and applied to everyone"),
'groups': fields.many2many('res.groups', 'rule_group_rel', 'rule_group_id', 'group_id', 'Groups'),
'domain_force': fields.text('Domain'),
- 'domain': fields.function(_domain_force_get, string='Domain', type='text'),
+ 'domain': fields.function(_domain_force_get, string='Domain', type='binary'),
'perm_read': fields.boolean('Apply for Read'),
'perm_write': fields.boolean('Apply for Write'),
'perm_create': fields.boolean('Apply for Create'),
@@ -127,7 +127,7 @@ class ir_rule(osv.osv):
group_domains = {} # map: group -> list of domains
for rule in self.browse(cr, SUPERUSER_ID, rule_ids):
# read 'domain' as UID to have the correct eval context for the rule.
- rule_domain = self.read(cr, uid, rule.id, ['domain'])['domain']
+ rule_domain = self.read(cr, uid, [rule.id], ['domain'])[0]['domain']
dom = expression.normalize_domain(rule_domain)
for group in rule.groups:
if group in user.groups_id:
diff --git a/openerp/addons/base/ir/ir_sequence.py b/openerp/addons/base/ir/ir_sequence.py
index a990a17f032..e589e2cae06 100644
--- a/openerp/addons/base/ir/ir_sequence.py
+++ b/openerp/addons/base/ir/ir_sequence.py
@@ -234,15 +234,15 @@ class ir_sequence(openerp.osv.osv.osv):
'sec': time.strftime('%S', t),
}
- def _next(self, cr, uid, seq_ids, context=None):
- if not seq_ids:
+ def _next(self, cr, uid, ids, context=None):
+ if not ids:
return False
if context is None:
context = {}
force_company = context.get('force_company')
if not force_company:
force_company = self.pool.get('res.users').browse(cr, uid, uid).company_id.id
- sequences = self.read(cr, uid, seq_ids, ['name','company_id','implementation','number_next','prefix','suffix','padding'])
+ sequences = self.read(cr, uid, ids, ['name','company_id','implementation','number_next','prefix','suffix','padding'])
preferred_sequences = [s for s in sequences if s['company_id'] and s['company_id'][0] == force_company ]
seq = preferred_sequences[0] if preferred_sequences else sequences[0]
if seq['implementation'] == 'standard':
@@ -251,6 +251,7 @@ class ir_sequence(openerp.osv.osv.osv):
else:
cr.execute("SELECT number_next FROM ir_sequence WHERE id=%s FOR UPDATE NOWAIT", (seq['id'],))
cr.execute("UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s ", (seq['id'],))
+ self.invalidate_cache(cr, uid, ['number_next'], [seq['id']], context=context)
d = self._interpolation_dict()
try:
interpolated_prefix = self._interpolate(seq['prefix'], d)
diff --git a/openerp/addons/base/ir/ir_translation.py b/openerp/addons/base/ir/ir_translation.py
index 7952c957caf..07732850180 100644
--- a/openerp/addons/base/ir/ir_translation.py
+++ b/openerp/addons/base/ir/ir_translation.py
@@ -168,11 +168,11 @@ class ir_translation(osv.osv):
else:
model_name, field = record.name.split(',')
model = self.pool.get(model_name)
- if model and model.exists(cr, uid, record.res_id, context=context):
+ if model is not None:
# Pass context without lang, need to read real stored field, not translation
context_no_lang = dict(context, lang=None)
- result = model.read(cr, uid, record.res_id, [field], context=context_no_lang)
- res[record.id] = result[field] if result else False
+ result = model.read(cr, uid, [record.res_id], [field], context=context_no_lang)
+ res[record.id] = result[0][field] if result else False
return res
def _set_src(self, cr, uid, id, name, value, args, context=None):
diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py
index 60014be9e01..d55451dd33c 100644
--- a/openerp/addons/base/ir/ir_ui_menu.py
+++ b/openerp/addons/base/ir/ir_ui_menu.py
@@ -21,15 +21,15 @@
##############################################################################
import base64
+import operator
import re
import threading
-import operator
-from openerp.tools.safe_eval import safe_eval as eval
-from openerp import tools
+
import openerp.modules
from openerp.osv import fields, osv
+from openerp import api, tools
+from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
-from openerp import SUPERUSER_ID
MENU_ITEM_SEPARATOR = "/"
@@ -38,71 +38,74 @@ class ir_ui_menu(osv.osv):
_name = 'ir.ui.menu'
def __init__(self, *args, **kwargs):
- self.cache_lock = threading.RLock()
- self._cache = {}
+ cls = type(self)
+ # by design, self._menu_cache is specific to the database
+ cls._menu_cache_lock = threading.RLock()
+ cls._menu_cache = {}
super(ir_ui_menu, self).__init__(*args, **kwargs)
self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache')
def clear_cache(self):
- with self.cache_lock:
+ with self._menu_cache_lock:
# radical but this doesn't frequently happen
- if self._cache:
+ if self._menu_cache:
# Normally this is done by openerp.tools.ormcache
# but since we do not use it, set it by ourself.
self.pool._any_cache_cleared = True
- self._cache = {}
+ self._menu_cache.clear()
- def _filter_visible_menus(self, cr, uid, ids, context=None):
- """Filters the give menu ids to only keep the menu items that should be
- visible in the menu hierarchy of the current user.
- Uses a cache for speeding up the computation.
+ @api.multi
+ @api.returns('self')
+ def _filter_visible_menus(self):
+ """ Filter `self` to only keep the menu items that should be visible in
+ the menu hierarchy of the current user.
+ Uses a cache for speeding up the computation.
"""
- with self.cache_lock:
- modelaccess = self.pool.get('ir.model.access')
- user_groups = set(self.pool.get('res.users').read(cr, SUPERUSER_ID, uid, ['groups_id'])['groups_id'])
- result = []
- for menu in self.browse(cr, uid, ids, context=context):
- # this key works because user access rights are all based on user's groups (cfr ir_model_access.check)
- key = (cr.dbname, menu.id, tuple(user_groups))
- if key in self._cache:
- if self._cache[key]:
- result.append(menu.id)
- #elif not menu.groups_id and not menu.action:
- # result.append(menu.id)
- continue
+ with self._menu_cache_lock:
+ groups = self.env.user.groups_id
- self._cache[key] = False
- if menu.groups_id:
- restrict_to_groups = [g.id for g in menu.groups_id]
- if not user_groups.intersection(restrict_to_groups):
- continue
- #result.append(menu.id)
- #self._cache[key] = True
- #continue
+ # visibility is entirely based on the user's groups;
+ # self._menu_cache[key] gives the ids of all visible menus
+ key = frozenset(groups._ids)
+ if key in self._menu_cache:
+ visible = self.browse(self._menu_cache[key])
- if menu.action:
- # we check if the user has access to the action of the menu
- data = menu.action
- if data:
- model_field = { 'ir.actions.act_window': 'res_model',
- 'ir.actions.report.xml': 'model',
- 'ir.actions.wizard': 'model',
- 'ir.actions.server': 'model_id',
- }
+ else:
+ # retrieve all menus, and determine which ones are visible
+ context = {'ir.ui.menu.full_list': True}
+ menus = self.with_context(context).search([])
- field = model_field.get(menu.action._name)
- if field and data[field]:
- if not modelaccess.check(cr, uid, data[field], 'read', False):
- continue
- else:
- # if there is no action, it's a 'folder' menu
- if not menu.child_id:
- # not displayed if there is no children
- continue
+ # first discard all menus with groups the user does not have
+ menus = menus.filtered(
+ lambda menu: not menu.groups_id or menu.groups_id & groups)
- result.append(menu.id)
- self._cache[key] = True
- return result
+ # take apart menus that have an action
+ action_menus = menus.filtered('action')
+ folder_menus = menus - action_menus
+ visible = self.browse()
+
+ # process action menus, check whether their action is allowed
+ access = self.env['ir.model.access']
+ model_fname = {
+ 'ir.actions.act_window': 'res_model',
+ 'ir.actions.report.xml': 'model',
+ 'ir.actions.wizard': 'model',
+ 'ir.actions.server': 'model_id',
+ }
+ for menu in action_menus:
+ fname = model_fname.get(menu.action._name)
+ if not fname or not menu.action[fname] or \
+ access.check(menu.action[fname], 'read', False):
+ # make menu visible, and its folder ancestors, too
+ visible += menu
+ menu = menu.parent_id
+ while menu and menu in folder_menus and menu not in visible:
+ visible += menu
+ menu = menu.parent_id
+
+ self._menu_cache[key] = visible._ids
+
+ return self.filtered(lambda menu: menu in visible)
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
if context is None:
@@ -155,13 +158,13 @@ class ir_ui_menu(osv.osv):
parent_path = ''
return parent_path + elmt.name
- def create(self, *args, **kwargs):
+ def create(self, cr, uid, values, context=None):
self.clear_cache()
- return super(ir_ui_menu, self).create(*args, **kwargs)
+ return super(ir_ui_menu, self).create(cr, uid, values, context=context)
- def write(self, *args, **kwargs):
+ def write(self, cr, uid, ids, values, context=None):
self.clear_cache()
- return super(ir_ui_menu, self).write(*args, **kwargs)
+ return super(ir_ui_menu, self).write(cr, uid, ids, values, context=context)
def unlink(self, cr, uid, ids, context=None):
# Detach children and promote them to top-level, because it would be unwise to
@@ -182,7 +185,7 @@ class ir_ui_menu(osv.osv):
def copy(self, cr, uid, id, default=None, context=None):
ir_values_obj = self.pool.get('ir.values')
- res = super(ir_ui_menu, self).copy(cr, uid, id, context=context)
+ res = super(ir_ui_menu, self).copy(cr, uid, id, default=default, context=context)
datas=self.read(cr,uid,[res],['name'])[0]
rex=re.compile('\([0-9]+\)')
concat=rex.findall(datas['name'])
diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py
index b5231808b5e..08fb4d4ef59 100644
--- a/openerp/addons/base/ir/ir_ui_view.py
+++ b/openerp/addons/base/ir/ir_ui_view.py
@@ -35,7 +35,7 @@ import HTMLParser
from lxml import etree
import openerp
-from openerp import tools
+from openerp import tools, api
from openerp.http import request
from openerp.osv import fields, osv, orm
from openerp.tools import graph, SKIPPED_ELEMENT_TYPES
@@ -302,15 +302,6 @@ class view(osv.osv):
view.application,
))
-
- def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({
- 'model_ids': [],
- })
- return super(view, self).copy(cr, uid, id, default, context=context)
-
# default view selection
def default_view(self, cr, uid, model, view_type, context=None):
""" Fetches the default view for the provided (model, view_type) pair:
@@ -603,7 +594,7 @@ class view(osv.osv):
modifiers = {}
Model = self.pool.get(model)
- if not Model:
+ if Model is None:
self.raise_view_error(cr, user, _('Model not found: %(model)s') % dict(model=model),
view_id, context)
@@ -622,10 +613,10 @@ class view(osv.osv):
:return: True if field should be included in the result of fields_view_get
"""
- if node.tag == 'field' and node.get('name') in Model._all_columns:
- column = Model._all_columns[node.get('name')].column
- if column.groups and not self.user_has_groups(
- cr, user, groups=column.groups, context=context):
+ if node.tag == 'field' and node.get('name') in Model._fields:
+ field = Model._fields[node.get('name')]
+ if field.groups and not self.user_has_groups(
+ cr, user, groups=field.groups, context=context):
node.getparent().remove(node)
fields.pop(node.get('name'), None)
# no point processing view-level ``groups`` anymore, return
@@ -662,15 +653,8 @@ class view(osv.osv):
fields = xfields
if node.get('name'):
attrs = {}
- try:
- if node.get('name') in Model._columns:
- column = Model._columns[node.get('name')]
- else:
- column = Model._inherit_fields[node.get('name')][2]
- except Exception:
- column = False
-
- if column:
+ field = Model._fields.get(node.get('name'))
+ if field:
children = False
views = {}
for f in node:
@@ -678,7 +662,7 @@ class view(osv.osv):
node.remove(f)
ctx = context.copy()
ctx['base_model_name'] = model
- xarch, xfields = self.postprocess_and_fields(cr, user, column._obj or None, f, view_id, ctx)
+ xarch, xfields = self.postprocess_and_fields(cr, user, field.comodel_name, f, view_id, ctx)
views[str(f.tag)] = {
'arch': xarch,
'fields': xfields
@@ -746,6 +730,36 @@ class view(osv.osv):
orm.transfer_modifiers_to_node(modifiers, node)
return fields
+ def add_on_change(self, cr, user, model_name, arch):
+ """ Add attribute on_change="1" on fields that are dependencies of
+ computed fields on the same view.
+ """
+ # map each field object to its corresponding nodes in arch
+ field_nodes = collections.defaultdict(list)
+
+ def collect(node, model):
+ if node.tag == 'field':
+ field = model._fields.get(node.get('name'))
+ if field:
+ field_nodes[field].append(node)
+ if field.relational:
+ model = self.pool.get(field.comodel_name)
+ for child in node:
+ collect(child, model)
+
+ collect(arch, self.pool[model_name])
+
+ for field, nodes in field_nodes.iteritems():
+ # if field should trigger an onchange, add on_change="1" on the
+ # nodes referring to field
+ model = self.pool[field.model_name]
+ if model._has_onchange(field, field_nodes):
+ for node in nodes:
+ if not node.get('on_change'):
+ node.set('on_change', '1')
+
+ return arch
+
def _disable_workflow_buttons(self, cr, user, model, node):
""" Set the buttons in node to readonly if the user can't activate them. """
if model is None or user == 1:
@@ -784,7 +798,7 @@ class view(osv.osv):
"""
fields = {}
Model = self.pool.get(model)
- if not Model:
+ if Model is None:
self.raise_view_error(cr, user, _('Model not found: %(model)s') % dict(model=model), view_id, context)
if node.tag == 'diagram':
@@ -800,6 +814,7 @@ class view(osv.osv):
else:
fields = Model.fields_get(cr, user, None, context)
+ node = self.add_on_change(cr, user, model, node)
fields_def = self.postprocess(cr, user, model, node, view_id, False, fields, context=context)
node = self._disable_workflow_buttons(cr, user, model, node)
if node.tag in ('kanban', 'tree', 'form', 'gantt'):
@@ -965,6 +980,7 @@ class view(osv.osv):
xmlid = imd.search_read(cr, uid, domain, ['module', 'name'])[0]
return '%s.%s' % (xmlid['module'], xmlid['name'])
+ @api.cr_uid_ids_context
def render(self, cr, uid, id_or_xml_id, values=None, engine='ir.qweb', context=None):
if isinstance(id_or_xml_id, list):
id_or_xml_id = id_or_xml_id[0]
diff --git a/openerp/addons/base/ir/ir_values.py b/openerp/addons/base/ir/ir_values.py
index 41d781ac68f..4720f601f49 100644
--- a/openerp/addons/base/ir/ir_values.py
+++ b/openerp/addons/base/ir/ir_values.py
@@ -20,6 +20,7 @@
##############################################################################
import pickle
+from openerp import tools
from openerp.osv import osv, fields
from openerp.osv.orm import except_orm
@@ -188,6 +189,21 @@ class ir_values(osv.osv):
if not cr.fetchone():
cr.execute('CREATE INDEX ir_values_key_model_key2_res_id_user_id_idx ON ir_values (key, model, key2, res_id, user_id)')
+ def create(self, cr, uid, vals, context=None):
+ res = super(ir_values, self).create(cr, uid, vals, context=context)
+ self.get_defaults_dict.clear_cache(self)
+ return res
+
+ def write(self, cr, uid, ids, vals, context=None):
+ res = super(ir_values, self).write(cr, uid, ids, vals, context=context)
+ self.get_defaults_dict.clear_cache(self)
+ return res
+
+ def unlink(self, cr, uid, ids, context=None):
+ res = super(ir_values, self).unlink(cr, uid, ids, context=context)
+ self.get_defaults_dict.clear_cache(self)
+ return res
+
def set_default(self, cr, uid, model, field_name, value, for_all_users=True, company_id=False, condition=False):
"""Defines a default value for the given model and field_name. Any previous
default for the same scope (model, field_name, value, for_all_users, company_id, condition)
@@ -319,6 +335,15 @@ class ir_values(osv.osv):
(row['id'], row['name'], pickle.loads(row['value'].encode('utf-8'))))
return defaults.values()
+ # use ormcache: this is called a lot by BaseModel.add_default_value()!
+ @tools.ormcache(skiparg=2)
+ def get_defaults_dict(self, cr, uid, model, condition=False):
+ """ Returns a dictionary mapping field names with their corresponding
+ default value. This method simply improves the returned value of
+ :meth:`~.get_defaults`.
+ """
+ return dict((f, v) for i, f, v in self.get_defaults(cr, uid, model, condition))
+
def set_action(self, cr, uid, name, action_slot, model, action, res_id=False):
"""Binds an the given action to the given model's action slot - for later
retrieval via :meth:`~.get_actions`. Any existing binding of the same action
@@ -395,9 +420,9 @@ class ir_values(osv.osv):
if not action['value']:
continue # skip if undefined
action_model_name, action_id = action['value'].split(',')
- action_model = self.pool.get(action_model_name)
- if not action_model:
+ if action_model_name not in self.pool:
continue # unknow model? skip it
+ action_model = self.pool[action_model_name]
fields = [field for field in action_model._all_columns if field not in EXCLUDED_FIELDS]
# FIXME: needs cleanup
try:
diff --git a/openerp/addons/base/module/module.py b/openerp/addons/base/module/module.py
index d0cb6b18f7b..89f3e9d006e 100644
--- a/openerp/addons/base/module/module.py
+++ b/openerp/addons/base/module/module.py
@@ -48,7 +48,8 @@ from openerp.modules.db import create_categories
from openerp.modules import get_module_resource
from openerp.tools.parse_version import parse_version
from openerp.tools.translate import _
-from openerp.osv import fields, osv, orm
+from openerp.osv import osv, orm, fields
+from openerp import api, fields as fields2
_logger = logging.getLogger(__name__)
@@ -374,34 +375,41 @@ class module(osv.osv):
msg = _('Unable to process module "%s" because an external dependency is not met: %s')
raise orm.except_orm(_('Error'), msg % (module_name, e.args[0]))
- def state_update(self, cr, uid, ids, newstate, states_to_update, context=None, level=100):
+ @api.multi
+ def state_update(self, newstate, states_to_update, level=100):
if level < 1:
raise orm.except_orm(_('Error'), _('Recursion error in modules dependencies !'))
+
+ # whether some modules are installed with demo data
demo = False
- for module in self.browse(cr, uid, ids, context=context):
- mdemo = False
+
+ for module in self:
+ # determine dependency modules to update/others
+ update_mods, ready_mods = self.browse(), self.browse()
for dep in module.dependencies_id:
if dep.state == 'unknown':
raise orm.except_orm(_('Error'), _("You try to install module '%s' that depends on module '%s'.\nBut the latter module is not available in your system.") % (module.name, dep.name,))
- ids2 = self.search(cr, uid, [('name', '=', dep.name)])
- if dep.state != newstate:
- mdemo = self.state_update(cr, uid, ids2, newstate, states_to_update, context, level - 1) or mdemo
+ if dep.depend_id.state == newstate:
+ ready_mods += dep.depend_id
else:
- od = self.browse(cr, uid, ids2)[0]
- mdemo = od.demo or mdemo
+ update_mods += dep.depend_id
+ # update dependency modules that require it, and determine demo for module
+ update_demo = update_mods.state_update(newstate, states_to_update, level=level-1)
+ module_demo = module.demo or update_demo or any(mod.demo for mod in ready_mods)
+ demo = demo or module_demo
+
+ # check dependencies and update module itself
self.check_external_dependencies(module.name, newstate)
- if not module.dependencies_id:
- mdemo = module.demo
if module.state in states_to_update:
- self.write(cr, uid, [module.id], {'state': newstate, 'demo': mdemo})
- demo = demo or mdemo
+ module.write({'state': newstate, 'demo': module_demo})
+
return demo
def button_install(self, cr, uid, ids, context=None):
# Mark the given modules to be installed.
- self.state_update(cr, uid, ids, 'to install', ['uninstalled'], context)
+ self.state_update(cr, uid, ids, 'to install', ['uninstalled'], context=context)
# Mark (recursively) the newly satisfied modules to also be installed
@@ -524,7 +532,7 @@ class module(osv.osv):
def button_upgrade(self, cr, uid, ids, context=None):
depobj = self.pool.get('ir.module.module.dependency')
- todo = self.browse(cr, uid, ids, context=context)
+ todo = list(self.browse(cr, uid, ids, context=context))
self.update_list(cr, uid)
i = 0
@@ -598,7 +606,7 @@ class module(osv.osv):
for key in values:
old = getattr(mod, key)
updated = isinstance(values[key], basestring) and tools.ustr(values[key]) or values[key]
- if not old == updated:
+ if (old or updated) and updated != old:
updated_values[key] = values[key]
if terp.get('installable', True) and mod.state == 'uninstallable':
updated_values['state'] = 'uninstalled'
@@ -726,6 +734,7 @@ class module(osv.osv):
cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%s, %s)', (mod_browse.id, dep))
for dep in (existing - needed):
cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s and name = %s', (mod_browse.id, dep))
+ self.invalidate_cache(cr, uid, ['dependencies_id'], [mod_browse.id])
def _update_category(self, cr, uid, mod_browse, category='Uncategorized'):
current_category = mod_browse.category_id
@@ -754,37 +763,47 @@ class module(osv.osv):
if not mod.description:
_logger.warning('module %s: description is empty !', mod.name)
-class module_dependency(osv.osv):
+
+DEP_STATES = [
+ ('uninstallable', 'Uninstallable'),
+ ('uninstalled', 'Not Installed'),
+ ('installed', 'Installed'),
+ ('to upgrade', 'To be upgraded'),
+ ('to remove', 'To be removed'),
+ ('to install', 'To be installed'),
+ ('unknown', 'Unknown'),
+]
+
+class module_dependency(osv.Model):
_name = "ir.module.module.dependency"
_description = "Module dependency"
- def _state(self, cr, uid, ids, name, args, context=None):
- result = {}
- mod_obj = self.pool.get('ir.module.module')
- for md in self.browse(cr, uid, ids):
- ids = mod_obj.search(cr, uid, [('name', '=', md.name)])
- if ids:
- result[md.id] = mod_obj.read(cr, uid, [ids[0]], ['state'])[0]['state']
- else:
- result[md.id] = 'unknown'
- return result
+ # the dependency name
+ name = fields2.Char(index=True)
- _columns = {
- # The dependency name
- 'name': fields.char('Name', select=True),
+ # the module that depends on it
+ module_id = fields2.Many2one('ir.module.module', 'Module', ondelete='cascade')
- # The module that depends on it
- 'module_id': fields.many2one('ir.module.module', 'Module', select=True, ondelete='cascade'),
+ # the module corresponding to the dependency, and its status
+ depend_id = fields2.Many2one('ir.module.module', 'Dependency', compute='_compute_depend')
+ state = fields2.Selection(DEP_STATES, string='Status', compute='_compute_state')
+
+ @api.multi
+ @api.depends('name')
+ def _compute_depend(self):
+ # retrieve all modules corresponding to the dependency names
+ names = list(set(dep.name for dep in self))
+ mods = self.env['ir.module.module'].search([('name', 'in', names)])
+
+ # index modules by name, and assign dependencies
+ name_mod = dict((mod.name, mod) for mod in mods)
+ for dep in self:
+ dep.depend_id = name_mod.get(dep.name)
+
+ @api.one
+ @api.depends('depend_id.state')
+ def _compute_state(self):
+ self.state = self.depend_id.state or 'unknown'
- 'state': fields.function(_state, type='selection', selection=[
- ('uninstallable', 'Uninstallable'),
- ('uninstalled', 'Not Installed'),
- ('installed', 'Installed'),
- ('to upgrade', 'To be upgraded'),
- ('to remove', 'To be removed'),
- ('to install', 'To be installed'),
- ('unknown', 'Unknown'),
- ], string='Status', readonly=True, select=True),
- }
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/module/wizard/base_import_language.py b/openerp/addons/base/module/wizard/base_import_language.py
index d8757605d64..bd554a47c5c 100644
--- a/openerp/addons/base/module/wizard/base_import_language.py
+++ b/openerp/addons/base/module/wizard/base_import_language.py
@@ -44,7 +44,7 @@ class base_language_import(osv.osv_memory):
context = {}
this = self.browse(cr, uid, ids[0])
if this.overwrite:
- context.update(overwrite=True)
+ context = dict(context, overwrite=True)
fileobj = TemporaryFile('w+')
try:
fileobj.write(base64.decodestring(this.data))
diff --git a/openerp/addons/base/module/wizard/base_module_update.py b/openerp/addons/base/module/wizard/base_module_update.py
index e2d8ce01d4a..2f3d0cc1af2 100644
--- a/openerp/addons/base/module/wizard/base_module_update.py
+++ b/openerp/addons/base/module/wizard/base_module_update.py
@@ -1,49 +1,23 @@
# -*- 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 .
-#
-##############################################################################
-from openerp.osv import osv, fields
-
-class base_module_update(osv.osv_memory):
- """ Update Module """
+from openerp import models, fields, api
+class base_module_update(models.TransientModel):
_name = "base.module.update"
_description = "Update Module"
- _columns = {
- 'update': fields.integer('Number of modules updated', readonly=True),
- 'add': fields.integer('Number of modules added', readonly=True),
- 'state':fields.selection([('init','init'),('done','done')], 'Status', readonly=True),
- }
+ updated = fields.Integer('Number of modules updated', readonly=True)
+ added = fields.Integer('Number of modules added', readonly=True)
+ state = fields.Selection([('init', 'init'), ('done', 'done')], 'Status', readonly=True, default='init')
- _defaults = {
- 'state': 'init',
- }
-
- def update_module(self, cr, uid, ids, context=None):
- module_obj = self.pool.get('ir.module.module')
- update, add = module_obj.update_list(cr, uid,)
- self.write(cr, uid, ids, {'update': update, 'add': add, 'state': 'done'}, context=context)
+ @api.one
+ def update_module(self):
+ self.updated, self.added = self.env['ir.module.module'].update_list()
+ self.state = 'done'
return False
- def action_module_open(self, cr, uid, ids, context):
+ @api.multi
+ def action_module_open(self):
res = {
'domain': str([]),
'name': 'Modules',
@@ -54,5 +28,3 @@ class base_module_update(osv.osv_memory):
'type': 'ir.actions.act_window',
}
return res
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/addons/base/module/wizard/base_module_update_view.xml b/openerp/addons/base/module/wizard/base_module_update_view.xml
index 3dccc061593..eff8e790677 100644
--- a/openerp/addons/base/module/wizard/base_module_update_view.xml
+++ b/openerp/addons/base/module/wizard/base_module_update_view.xml
@@ -13,8 +13,8 @@
-
-
+
+
Introduction to something
-Teaching Assistants
@@ -41,26 +36,24 @@ diff --git a/controllers/academy.py b/controllers/academy.py -""" % { - 'tas': '\n'.join(tas) - } -- -+ return http.request.registry['ir.ui.view'].render(cr, uid, 'academy.index', { ++ return http.request.render('academy.index', { + 'tas': '\n'.join(tas) -+ }, context=context) ++ }) + @http.route('/tas/%(name)s
- - -""" % teaching_assistants[id] -+ cr, uid, context = http.request.cr, http.request.uid, http.request.context -+ return http.request.registry['ir.ui.view'].render( -+ cr, uid, 'academy.ta', teaching_assistants[id], context=context) ++ return http.request.render('academy.ta', teaching_assistants[id]) diff --git a/views/templates.xml b/views/templates.xml new file mode 100644 --- /dev/null @@ -72,7 +65,7 @@ new file mode 100644 + + +Introduction to a thing
@@ -95,7 +88,7 @@ new file mode 100644 + + +
diff --git a/doc/howto/howto_website/website-dependency b/doc/howto/howto_website/website-dependency index 37c14e8f2c2..e1a038233ca 100644 --- a/doc/howto/howto_website/website-dependency +++ b/doc/howto/howto_website/website-dependency @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 0fdf3e29ce5bb1dd39479f157eeac5bdfd3cffb0 +# Parent 9d2ca56532b5bbb5d6c9de995088d895fbefff2e diff --git a/__openerp__.py b/__openerp__.py --- a/__openerp__.py @@ -16,32 +16,20 @@ diff --git a/__openerp__.py b/__openerp__.py diff --git a/controllers/academy.py b/controllers/academy.py --- a/controllers/academy.py +++ b/controllers/academy.py -@@ -12,19 +12,12 @@ teaching_assistants = [ - ] - +@@ -14,12 +14,8 @@ teaching_assistants = [ class academy(main.Home): -- @http.route('/', auth='none') -+ @http.route('/', auth='none', website=True) + @http.route('/', auth='none') def index(self): -- cr, uid, context = http.request.cr, http.request.uid, http.request.context - tas = [ - 'Introduction to a thing
@@ -83,7 +83,7 @@ diff --git a/views/templates.xml b/views/templates.xml - - -
diff --git a/doc/index.rst b/doc/index.rst index ad42e41a10f..f7cd7c399f1 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -36,9 +36,10 @@ OpenERP Server API .. toctree:: :maxdepth: 1 - orm-methods.rst - api_models.rst - routing.rst + new_api + orm-methods + api_models + routing Changelog ''''''''' diff --git a/doc/new_api.rst b/doc/new_api.rst new file mode 100644 index 00000000000..e99d5b7eef4 --- /dev/null +++ b/doc/new_api.rst @@ -0,0 +1,138 @@ +================== +High-level ORM API +================== + +.. _compute: + +Computed fields: defaults and function fields +============================================= + +The high-level API attempts to unify concepts of programmatic value generation +for function fields (stored or not) and default values through the use of +computed fields. + +Fields are marked as computed by setting their ``compute`` attribute to the +name of the method used to compute then:: + + has_sibling = fields.Integer(compute='compute_has_sibling') + +by default computation methods behave as simple defaults in case no +corresponding value is found in the database:: + + def default_number_of_employees(self): + self.number_of_employees = 1 + +.. todo:: + + literal defaults:: + + has_sibling = fields.Integer(compute=fields.default(1)) + +but they can also be used for computed fields by specifying fields used for +the computation. The dependencies can be dotted for "cascading" through +related models:: + + @api.depends('parent_id.children_count') + def compute_has_sibling(self): + self.has_sibling = self.parent_id.children_count >= 2 + +.. todo:: + + function-based:: + + has_sibling = fields.Integer() + @has_sibling.computer + @api.depends('parent_id.children_count') + def compute_has_sibling(self): + self.has_sibling = self.parent_id.children_count >= 2 + +note that computation methods (defaults or others) do not *return* a value, +they *set* values the current object. This means the high-level API does not +need :ref:`an explicit multi