[MERGE] forward port of branch saas-2 up to revid 9092 dle@openerp.com-20140115165506-yuux9km39gbv7k4n

bzr revid: chs@openerp.com-20140115214454-dhl8xzr429co0j5d
This commit is contained in:
Christophe Simonis 2014-01-15 22:44:54 +01:00
commit 91aaaec0a5
40 changed files with 231 additions and 123 deletions

View File

@ -1023,7 +1023,10 @@ class account_period(osv.osv):
if not result: if not result:
result = self.search(cr, uid, args, context=context) result = self.search(cr, uid, args, context=context)
if not result: if not result:
raise osv.except_osv(_('Error!'), _('There is no period defined for this date: %s.\nPlease create one.')%dt) model, action_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'action_account_fiscalyear')
msg = _('There is no period defined for this date: %s.\nPlease, go to Configuration/Periods and configure a fiscal year.') % dt
raise openerp.exceptions.RedirectWarning(msg, action_id, _('Go to the configuration panel'))
return result return result
def action_draft(self, cr, uid, ids, *args): def action_draft(self, cr, uid, ids, *args):

View File

@ -849,18 +849,17 @@ class account_move_line(osv.osv):
(tuple(ids), )) (tuple(ids), ))
r = cr.fetchall() r = cr.fetchall()
#TODO: move this check to a constraint in the account_move_reconcile object #TODO: move this check to a constraint in the account_move_reconcile object
if len(r) != 1:
raise osv.except_osv(_('Error'), _('Entries are not of the same account or already reconciled ! '))
if not unrec_lines: if not unrec_lines:
raise osv.except_osv(_('Error!'), _('Entry is already reconciled.')) raise osv.except_osv(_('Error!'), _('Entry is already reconciled.'))
account = account_obj.browse(cr, uid, account_id, context=context) account = account_obj.browse(cr, uid, account_id, context=context)
if not account.reconcile:
raise osv.except_osv(_('Error'), _('The account is not defined to be reconciled !'))
if r[0][1] != None: if r[0][1] != None:
raise osv.except_osv(_('Error!'), _('Some entries are already reconciled.')) raise osv.except_osv(_('Error!'), _('Some entries are already reconciled.'))
if context.get('fy_closing'): if (not currency_obj.is_zero(cr, uid, account.company_id.currency_id, writeoff)) or \
# We don't want to generate any write-off when being called from the
# wizard used to close a fiscal year (and it doesn't give us any
# writeoff_acc_id).
pass
elif (not currency_obj.is_zero(cr, uid, account.company_id.currency_id, writeoff)) or \
(account.currency_id and (not currency_obj.is_zero(cr, uid, account.currency_id, currency))): (account.currency_id and (not currency_obj.is_zero(cr, uid, account.currency_id, currency))):
if not writeoff_acc_id: if not writeoff_acc_id:
raise osv.except_osv(_('Warning!'), _('You have to provide an account for the write off/exchange difference entry.')) raise osv.except_osv(_('Warning!'), _('You have to provide an account for the write off/exchange difference entry.'))
@ -1199,7 +1198,7 @@ class account_move_line(osv.osv):
break break
# Automatically convert in the account's secondary currency if there is one and # Automatically convert in the account's secondary currency if there is one and
# the provided values were not already multi-currency # the provided values were not already multi-currency
if account.currency_id and (vals.get('amount_currency', False) is False) and account.currency_id.id != account.company_id.currency_id.id: if account.currency_id and 'amount_currency' not in vals and account.currency_id.id != account.company_id.currency_id.id:
vals['currency_id'] = account.currency_id.id vals['currency_id'] = account.currency_id.id
ctx = {} ctx = {}
if 'date' in vals: if 'date' in vals:

View File

@ -224,14 +224,6 @@ class account_fiscalyear_close(osv.osv_memory):
query_2nd_part = "" query_2nd_part = ""
query_2nd_part_args = [] query_2nd_part_args = []
for account in obj_acc_account.browse(cr, uid, account_ids, context={'fiscalyear': fy_id}): for account in obj_acc_account.browse(cr, uid, account_ids, context={'fiscalyear': fy_id}):
balance_in_currency = 0.0
if account.currency_id:
cr.execute('SELECT sum(COALESCE(amount_currency,0.0)) as balance_in_currency FROM account_move_line ' \
'WHERE account_id = %s ' \
'AND ' + query_line + ' ' \
'AND currency_id = %s', (account.id, account.currency_id.id))
balance_in_currency = cr.dictfetchone()['balance_in_currency']
company_currency_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.currency_id company_currency_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.currency_id
if not currency_obj.is_zero(cr, uid, company_currency_id, abs(account.balance)): if not currency_obj.is_zero(cr, uid, company_currency_id, abs(account.balance)):
if query_2nd_part: if query_2nd_part:
@ -246,7 +238,7 @@ class account_fiscalyear_close(osv.osv_memory):
period.id, period.id,
account.id, account.id,
account.currency_id and account.currency_id.id or None, account.currency_id and account.currency_id.id or None,
balance_in_currency, account.foreign_balance if account.currency_id else 0.0,
account.company_id.id, account.company_id.id,
'draft') 'draft')
if query_2nd_part: if query_2nd_part:

View File

@ -43,6 +43,7 @@
parent_id: account.cash parent_id: account.cash
type: other type: other
user_type: account.data_account_type_asset user_type: account.data_account_type_asset
reconcile: True
- -
Configure Creditor Account Payable. Configure Creditor Account Payable.
- -
@ -52,6 +53,7 @@
parent_id: account.a_pay parent_id: account.a_pay
type: other type: other
user_type: account.data_account_type_payable user_type: account.data_account_type_payable
reconcile: True
- -
Configure Debtor Account Receivable. Configure Debtor Account Receivable.
- -
@ -61,6 +63,7 @@
parent_id: account.a_recv parent_id: account.a_recv
type: other type: other
user_type: account.data_account_type_receivable user_type: account.data_account_type_receivable
reconcile: True
- -
Configure Cost of Good sale Account. Configure Cost of Good sale Account.
- -

View File

@ -43,6 +43,7 @@
parent_id: account.cash parent_id: account.cash
type: other type: other
user_type: account.data_account_type_asset user_type: account.data_account_type_asset
reconcile: True
- -
Configure Creditor Account Payable. Configure Creditor Account Payable.
- -
@ -52,6 +53,7 @@
parent_id: account.a_pay parent_id: account.a_pay
type: other type: other
user_type: account.data_account_type_payable user_type: account.data_account_type_payable
reconcile: True
- -
Configure Debtor Account Receivable. Configure Debtor Account Receivable.
- -
@ -61,6 +63,7 @@
parent_id: account.a_recv parent_id: account.a_recv
type: other type: other
user_type: account.data_account_type_receivable user_type: account.data_account_type_receivable
reconcile: True
- -
Configure Cost of Good sale Account. Configure Cost of Good sale Account.
- -

View File

@ -217,7 +217,7 @@ class account_voucher(osv.osv):
if context.get('type', 'sale') in ('purchase', 'payment'): if context.get('type', 'sale') in ('purchase', 'payment'):
nodes = doc.xpath("//field[@name='partner_id']") nodes = doc.xpath("//field[@name='partner_id']")
for node in nodes: for node in nodes:
node.set('context', "{'search_default_supplier': 1}") node.set('context', "{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1}")
if context.get('invoice_type','') in ('in_invoice', 'in_refund'): if context.get('invoice_type','') in ('in_invoice', 'in_refund'):
node.set('string', _("Supplier")) node.set('string', _("Supplier"))
res['arch'] = etree.tostring(doc) res['arch'] = etree.tostring(doc)
@ -1329,7 +1329,7 @@ class account_voucher(osv.osv):
'date': voucher.date, 'date': voucher.date,
'credit': diff > 0 and diff or 0.0, 'credit': diff > 0 and diff or 0.0,
'debit': diff < 0 and -diff or 0.0, 'debit': diff < 0 and -diff or 0.0,
'amount_currency': company_currency <> current_currency and (sign * -1 * voucher.writeoff_amount) or False, 'amount_currency': company_currency <> current_currency and (sign * -1 * voucher.writeoff_amount) or 0.0,
'currency_id': company_currency <> current_currency and current_currency or False, 'currency_id': company_currency <> current_currency and current_currency or False,
'analytic_account_id': voucher.analytic_id and voucher.analytic_id.id or False, 'analytic_account_id': voucher.analytic_id and voucher.analytic_id.id or False,
} }

View File

@ -194,7 +194,7 @@ class account_analytic_account(osv.osv):
'user_id': fields.many2one('res.users', 'Project Manager', track_visibility='onchange'), 'user_id': fields.many2one('res.users', 'Project Manager', track_visibility='onchange'),
'manager_id': fields.many2one('res.users', 'Account Manager', track_visibility='onchange'), 'manager_id': fields.many2one('res.users', 'Account Manager', track_visibility='onchange'),
'date_start': fields.date('Start Date'), 'date_start': fields.date('Start Date'),
'date': fields.date('End Date', select=True, track_visibility='onchange'), '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. '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'),
'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 '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

View File

@ -1049,11 +1049,13 @@ class crm_lead(format_address, osv.osv):
def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context=None): def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context=None):
phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0] phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0]
if action == 'log': if action == 'log':
prefix = 'Logged' message = _('Logged a call for %(date)s. %(description)s')
else: else:
prefix = 'Scheduled' message = _('Scheduled a call for %(date)s. %(description)s')
suffix = ' %s' % phonecall.description phonecall_date = datetime.strptime(phonecall.date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
message = _("%s a call for %s.%s") % (prefix, phonecall.date, suffix) phonecall_usertime = fields.datetime.context_timestamp(cr, uid, phonecall_date, context=context).strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
html_time = "<time datetime='%s+00:00'>%s</time>" % (phonecall.date, phonecall_usertime)
message = message % dict(date=html_time, description=phonecall.description)
return self.message_post(cr, uid, ids, body=message, context=context) return self.message_post(cr, uid, ids, body=message, context=context)
def log_meeting(self, cr, uid, ids, meeting_subject, meeting_date, duration, context=None): def log_meeting(self, cr, uid, ids, meeting_subject, meeting_date, duration, context=None):

View File

@ -1,11 +1,16 @@
openerp.crm_partner_assign = function (instance) { openerp.crm_partner_assign = function (instance) {
instance.crm_partner_assign = instance.crm_partner_assign || {}; instance.crm_partner_assign = instance.crm_partner_assign || {};
instance.crm_partner_assign.next_or_list = function(parent) { instance.crm_partner_assign.next_or_list = function(parent) {
var form = parent.inner_widget.views.form.controller; if (parent.inner_widget.active_view === "form"){
form.dataset.remove_ids([form.dataset.ids[form.dataset.index]]); var form = parent.inner_widget.views.form.controller;
form.reload(); form.dataset.remove_ids([form.dataset.ids[form.dataset.index]]);
if (!form.dataset.ids.length){ form.reload();
parent.inner_widget.switch_mode('list'); if (!form.dataset.ids.length){
parent.inner_widget.switch_mode('list');
}
}
else{
parent.inner_widget.views[parent.inner_widget.active_view].controller.reload();
} }
parent.do_action({ type: 'ir.actions.act_window_close' }); parent.do_action({ type: 'ir.actions.act_window_close' });
}; };

View File

@ -53,7 +53,7 @@ class crm_lead_forward_to_partner(osv.TransientModel):
values = {'partner_assigned_id': False} values = {'partner_assigned_id': False}
user = self.pool.get('res.users').browse(cr, uid, uid, context=context) user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
partner_ids = self.pool.get('res.partner').search(cr, SUPERUSER_ID, [('id', 'child_of', user.partner_id.commercial_partner_id.id)], context=context) partner_ids = self.pool.get('res.partner').search(cr, SUPERUSER_ID, [('id', 'child_of', user.partner_id.commercial_partner_id.id)], context=context)
lead_obj.message_unsubscribe(cr, SUPERUSER_ID, context.get('active_ids'), partner_ids, context=None) lead_obj.message_unsubscribe(cr, SUPERUSER_ID, context.get('active_ids', []), partner_ids, context=None)
try: try:
stage_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'crm_partner_assign', stage)[1] stage_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'crm_partner_assign', stage)[1]
except ValueError: except ValueError:
@ -62,11 +62,12 @@ class crm_lead_forward_to_partner(osv.TransientModel):
values.update({'stage_id': stage_id}) values.update({'stage_id': stage_id})
if wizard.comment: if wizard.comment:
message += '<p>%s</p>' % wizard.comment message += '<p>%s</p>' % wizard.comment
lead_obj.message_post(cr, uid, context.get('active_ids'), body=message, context=context) for active_id in context.get('active_ids', []):
lead_obj.message_post(cr, uid, active_id, body=message, context=context)
if values: if values:
lead_obj.write(cr, SUPERUSER_ID, context.get('active_ids'), values) lead_obj.write(cr, SUPERUSER_ID, context.get('active_ids', []), values)
if wizard.interested: if wizard.interested:
for lead in lead_obj.browse(cr, uid, context.get('active_ids'), context=context): for lead in lead_obj.browse(cr, uid, context.get('active_ids', []), context=context):
lead_obj.convert_opportunity(cr, SUPERUSER_ID, [lead.id], lead.partner_id and lead.partner_id.id or None, context=None) lead_obj.convert_opportunity(cr, SUPERUSER_ID, [lead.id], lead.partner_id and lead.partner_id.id or None, context=None)
return { return {
'type': 'ir.actions.client', 'type': 'ir.actions.client',

View File

@ -139,6 +139,8 @@ class crm_lead_forward_to_partner(osv.TransientModel):
values = {'partner_assigned_id': partner_id, 'user_id': partner_leads['partner'].user_id.id} values = {'partner_assigned_id': partner_id, 'user_id': partner_leads['partner'].user_id.id}
if stage_id: if stage_id:
values['stage_id'] = stage_id values['stage_id'] = stage_id
if partner_leads['partner'].user_id:
values['section_id'] = partner_leads['partner'].user_id.default_section_id.id
lead_obj.write(cr, uid, lead_ids, values) lead_obj.write(cr, uid, lead_ids, values)
self.pool.get('crm.lead').message_subscribe(cr, uid, lead_ids, [partner_id], context=context) self.pool.get('crm.lead').message_subscribe(cr, uid, lead_ids, [partner_id], context=context)
return True return True

View File

@ -23,6 +23,7 @@ import openerp
from openerp import SUPERUSER_ID from openerp import SUPERUSER_ID
from openerp import tools from openerp import tools
from openerp.osv import osv, fields from openerp.osv import osv, fields
from openerp.modules.registry import RegistryManager
class decimal_precision(osv.osv): class decimal_precision(osv.osv):
_name = 'decimal.precision' _name = 'decimal.precision'
@ -44,23 +45,28 @@ class decimal_precision(osv.osv):
res = cr.fetchone() res = cr.fetchone()
return res[0] if res else 2 return res[0] if res else 2
def clear_cache(self, cr):
"""clear cache and update models. Notify other workers to restart their registry."""
self.precision_get.clear_cache(self)
for obj in self.pool.obj_list():
for colname, col in self.pool.get(obj)._columns.items():
if isinstance(col, (fields.float, fields.function)):
col.digits_change(cr)
RegistryManager.signal_registry_change(cr.dbname)
def create(self, cr, uid, data, context=None): def create(self, cr, uid, data, context=None):
res = super(decimal_precision, self).create(cr, uid, data, context=context) res = super(decimal_precision, self).create(cr, uid, data, context=context)
self.precision_get.clear_cache(self) self.clear_cache(cr)
return res return res
def unlink(self, cr, uid, ids, context=None): def unlink(self, cr, uid, ids, context=None):
res = super(decimal_precision, self).unlink(cr, uid, ids, context=context) res = super(decimal_precision, self).unlink(cr, uid, ids, context=context)
self.precision_get.clear_cache(self) self.clear_cache(cr)
return res return res
def write(self, cr, uid, ids, data, *args, **argv): def write(self, cr, uid, ids, data, *args, **argv):
res = super(decimal_precision, self).write(cr, uid, ids, data, *args, **argv) res = super(decimal_precision, self).write(cr, uid, ids, data, *args, **argv)
self.precision_get.clear_cache(self) self.clear_cache(cr)
for obj in self.pool.obj_list():
for colname, col in self.pool[obj]._columns.items():
if isinstance(col, (fields.float, fields.function)):
col.digits_change(cr)
return res return res

View File

@ -64,7 +64,7 @@ class test_message_compose(TestMail):
'body_html': '${object.description}', 'body_html': '${object.description}',
'user_signature': True, 'user_signature': True,
'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])], 'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])],
'email_to': 'b@b.b c@c.c', 'email_to': 'b@b.b, c@c.c',
'email_cc': 'd@d.d' 'email_cc': 'd@d.d'
}) })
@ -192,7 +192,7 @@ class test_message_compose(TestMail):
email_template.write(cr, uid, [email_template_id], { email_template.write(cr, uid, [email_template_id], {
'model_id': user_model_id, 'model_id': user_model_id,
'body_html': '${object.login}', 'body_html': '${object.login}',
'email_to': '${object.email} c@c', 'email_to': '${object.email}, c@c',
'partner_to': '%i,%i' % (p_b_id, p_c_id), 'partner_to': '%i,%i' % (p_b_id, p_c_id),
'email_cc': 'd@d', 'email_cc': 'd@d',
}) })
@ -217,7 +217,7 @@ class test_message_compose(TestMail):
'subject': '${object.name}', 'subject': '${object.name}',
'body_html': '${object.description}', 'body_html': '${object.description}',
'user_signature': True, 'user_signature': True,
'email_to': 'b@b.b c@c.c', 'email_to': 'b@b.b, c@c.c',
'email_cc': 'd@d.d', 'email_cc': 'd@d.d',
'partner_to': '${user.partner_id.id},%s,%s,-1' % (self.user_raoul.partner_id.id, self.user_bert.partner_id.id) 'partner_to': '${user.partner_id.id},%s,%s,-1' % (self.user_raoul.partner_id.id, self.user_bert.partner_id.id)
}) })
@ -226,7 +226,7 @@ class test_message_compose(TestMail):
msg_id = email_template.send_mail(cr, uid, email_template_id, self.group_pigs_id, context=context) msg_id = email_template.send_mail(cr, uid, email_template_id, self.group_pigs_id, context=context)
mail = self.mail_mail.browse(cr, uid, msg_id, context=context) mail = self.mail_mail.browse(cr, uid, msg_id, context=context)
self.assertEqual(mail.subject, 'Pigs', 'email_template: send_mail: wrong subject') self.assertEqual(mail.subject, 'Pigs', 'email_template: send_mail: wrong subject')
self.assertEqual(mail.email_to, 'b@b.b c@c.c', 'email_template: send_mail: wrong email_to') self.assertEqual(mail.email_to, 'b@b.b, c@c.c', 'email_template: send_mail: wrong email_to')
self.assertEqual(mail.email_cc, 'd@d.d', 'email_template: send_mail: wrong email_cc') self.assertEqual(mail.email_cc, 'd@d.d', 'email_template: send_mail: wrong email_cc')
self.assertEqual( self.assertEqual(
set([partner.id for partner in mail.recipient_ids]), set([partner.id for partner in mail.recipient_ids]),

View File

@ -137,7 +137,7 @@ class mail_compose_message(osv.TransientModel):
def _get_or_create_partners_from_values(self, cr, uid, rendered_values, context=None): def _get_or_create_partners_from_values(self, cr, uid, rendered_values, context=None):
""" Check for email_to, email_cc, partner_to """ """ Check for email_to, email_cc, partner_to """
partner_ids = [] partner_ids = []
mails = tools.email_split(rendered_values.pop('email_to', '') + ' ' + rendered_values.pop('email_cc', '')) mails = tools.email_split(rendered_values.pop('email_to', '')) + tools.email_split(rendered_values.pop('email_cc', ''))
for mail in mails: for mail in mails:
partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context) partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context)
partner_ids.append(partner_id) partner_ids.append(partner_id)

View File

@ -120,7 +120,7 @@ class config(osv.Model):
res['url'] = content['alternateLink'] res['url'] = content['alternateLink']
key = self._get_key_from_url(res['url']) key = self._get_key_from_url(res['url'])
request_url = "https://www.googleapis.com/drive/v2/files/%s/permissions?emailMessage=This+is+a+drive+file+created+by+OpenERP&sendNotificationEmails=false&access_token=%s" % (key, access_token) request_url = "https://www.googleapis.com/drive/v2/files/%s/permissions?emailMessage=This+is+a+drive+file+created+by+OpenERP&sendNotificationEmails=false&access_token=%s" % (key, access_token)
data = {'role': 'reader', 'type': 'anyone', 'value': '', 'withLink': True} data = {'role': 'writer', 'type': 'anyone', 'value': '', 'withLink': True}
try: try:
req = urllib2.Request(request_url, json.dumps(data), headers) req = urllib2.Request(request_url, json.dumps(data), headers)
urllib2.urlopen(req) urllib2.urlopen(req)
@ -133,7 +133,7 @@ class config(osv.Model):
req = urllib2.Request(request_url, json.dumps(data), headers) req = urllib2.Request(request_url, json.dumps(data), headers)
urllib2.urlopen(req) urllib2.urlopen(req)
except urllib2.HTTPError: except urllib2.HTTPError:
raise self.pool.get('res.config.settings').get_config_warning(cr, _("The permission 'writer' for your email '%s' has not been written on the document. Is this email a valid Google Account ?" % user.email), context=context) pass
return res return res
def get_google_drive_config(self, cr, uid, res_model, res_id, context=None): def get_google_drive_config(self, cr, uid, res_model, res_id, context=None):

View File

@ -76,10 +76,11 @@ class account_analytic_account(osv.osv):
def on_change_partner_id(self, cr, uid, ids, partner_id, name, context=None): def on_change_partner_id(self, cr, uid, ids, partner_id, name, context=None):
res = super(account_analytic_account, self).on_change_partner_id(cr, uid, ids, partner_id, name, context=context) res = super(account_analytic_account, self).on_change_partner_id(cr, uid, ids, partner_id, name, context=context)
part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) if partner_id:
pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
if pricelist: pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False
res['value']['pricelist_id'] = pricelist if pricelist:
res['value']['pricelist_id'] = pricelist
return res return res
def set_close(self, cr, uid, ids, context=None): def set_close(self, cr, uid, ids, context=None):

View File

@ -9,12 +9,14 @@ openerp.hr_timesheet_sheet = function(instance) {
}, },
init: function() { init: function() {
this._super.apply(this, arguments); this._super.apply(this, arguments);
var self = this;
this.set({ this.set({
sheets: [], sheets: [],
date_to: false, date_to: false,
date_from: false, date_from: false,
}); });
this.updating = false; this.updating = false;
this.defs = [];
this.field_manager.on("field_changed:timesheet_ids", this, this.query_sheets); this.field_manager.on("field_changed:timesheet_ids", this, this.query_sheets);
this.field_manager.on("field_changed:date_from", this, function() { this.field_manager.on("field_changed:date_from", this, function() {
this.set({"date_from": instance.web.str_to_date(this.field_manager.get_field_value("date_from"))}); this.set({"date_from": instance.web.str_to_date(this.field_manager.get_field_value("date_from"))});
@ -29,6 +31,14 @@ openerp.hr_timesheet_sheet = function(instance) {
this.res_o2m_drop = new instance.web.DropMisordered(); this.res_o2m_drop = new instance.web.DropMisordered();
this.render_drop = new instance.web.DropMisordered(); this.render_drop = new instance.web.DropMisordered();
this.description_line = _t("/"); this.description_line = _t("/");
// Original save function is overwritten in order to wait all running deferreds to be done before actually applying the save.
this.view.original_save = _.bind(this.view.save, this.view);
this.view.save = function(prepend_on_create){
self.prepend_on_create = prepend_on_create;
return $.when.apply($, self.defs).then(function(){
return self.view.original_save(self.prepend_on_create);
});
};
}, },
go_to: function(event) { go_to: function(event) {
var id = JSON.parse($(event.target).data("id")); var id = JSON.parse($(event.target).data("id"));
@ -192,11 +202,11 @@ openerp.hr_timesheet_sheet = function(instance) {
account.days[day_count].lines[0].unit_amount += num - self.sum_box(account, day_count); account.days[day_count].lines[0].unit_amount += num - self.sum_box(account, day_count);
var product = (account.days[day_count].lines[0].product_id instanceof Array) ? account.days[day_count].lines[0].product_id[0] : account.days[day_count].lines[0].product_id var product = (account.days[day_count].lines[0].product_id instanceof Array) ? account.days[day_count].lines[0].product_id[0] : account.days[day_count].lines[0].product_id
var journal = (account.days[day_count].lines[0].journal_id instanceof Array) ? account.days[day_count].lines[0].journal_id[0] : account.days[day_count].lines[0].journal_id var journal = (account.days[day_count].lines[0].journal_id instanceof Array) ? account.days[day_count].lines[0].journal_id[0] : account.days[day_count].lines[0].journal_id
new instance.web.Model("hr.analytic.timesheet").call("on_change_unit_amount", [[], product, account.days[day_count].lines[0].unit_amount, false, false, journal]).then(function(res) { self.defs.push(new instance.web.Model("hr.analytic.timesheet").call("on_change_unit_amount", [[], product, account.days[day_count].lines[0].unit_amount, false, false, journal]).then(function(res) {
account.days[day_count].lines[0]['amount'] = res.value.amount || 0; account.days[day_count].lines[0]['amount'] = res.value.amount || 0;
self.display_totals(); self.display_totals();
self.sync(); self.sync();
}); }));
if(!isNaN($(this).val())){ if(!isNaN($(this).val())){
$(this).val(self.sum_box(account, day_count, true)); $(this).val(self.sum_box(account, day_count, true));
} }

View File

@ -29,7 +29,7 @@ import openerp.tools.config
import openerp.modules.registry import openerp.modules.registry
from openerp import http from openerp import http
from openerp.http import request from openerp.http import request
from openerp.osv import osv, fields from openerp.osv import osv, fields, expression
from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -249,11 +249,31 @@ class im_user(osv.osv):
res[obj["id"]] = obj["im_last_status"] and (last_update + delta) > current res[obj["id"]] = obj["im_last_status"] and (last_update + delta) > current
return res return res
def _status_search(self, cr, uid, obj, name, domain, context=None):
current = datetime.datetime.now()
delta = datetime.timedelta(0, DISCONNECTION_TIMER)
field, operator, value = domain[0]
if operator in expression.NEGATIVE_TERM_OPERATORS:
value = not value
if value:
return ['&', ('im_last_status', '=', True), ('im_last_status_update', '>', (current - delta).strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
else:
return ['|', ('im_last_status', '=', False), ('im_last_status_update', '<=', (current - delta).strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
def search_users(self, cr, uid, text_search, fields, limit, context=None): def search_users(self, cr, uid, text_search, fields, limit, context=None):
my_id = self.get_my_id(cr, uid, None, context) my_id = self.get_my_id(cr, uid, None, context)
found = self.search(cr, uid, [["name", "ilike", text_search], ["id", "<>", my_id], ["uuid", "=", False]], group_employee = self.pool['ir.model.data'].get_object_reference(cr, uid, 'base', 'group_user')[1]
found = self.search(cr, uid, [["name", "ilike", text_search], ["id", "<>", my_id], ["uuid", "=", False], ["im_status", "=", True], ["user_id.groups_id", "in", [group_employee]]],
order="name asc", limit=limit, context=context) order="name asc", limit=limit, context=context)
return self.read(cr, uid, found, fields, context=context) if len(found) < limit:
found += self.search(cr, uid, [["name", "ilike", text_search], ["id", "<>", my_id], ["uuid", "=", False], ["im_status", "=", True], ["id", "not in", found]],
order="name asc", limit=limit, context=context)
if len(found) < limit:
found += self.search(cr, uid, [["name", "ilike", text_search], ["id", "<>", my_id], ["uuid", "=", False], ["im_status", "=", False], ["id", "not in", found]],
order="name asc", limit=limit-len(found), context=context)
users = self.read(cr, uid, found, fields, context=context)
users.sort(key=lambda obj: found.index(obj['id']))
return users
def im_connect(self, cr, uid, uuid=None, context=None): def im_connect(self, cr, uid, uuid=None, context=None):
assert_uuid(uuid) assert_uuid(uuid)
@ -308,7 +328,7 @@ class im_user(osv.osv):
'im_last_received': fields.integer(string="Instant Messaging Last Received Message"), 'im_last_received': fields.integer(string="Instant Messaging Last Received Message"),
'im_last_status': fields.boolean(strint="Instant Messaging Last Status"), 'im_last_status': fields.boolean(strint="Instant Messaging Last Status"),
'im_last_status_update': fields.datetime(string="Instant Messaging Last Status Update"), 'im_last_status_update': fields.datetime(string="Instant Messaging Last Status Update"),
'im_status': fields.function(_im_status, string="Instant Messaging Status", type='boolean'), 'im_status': fields.function(_im_status, string="Instant Messaging Status", type='boolean', fnct_search=_status_search),
} }
_defaults = { _defaults = {

Binary file not shown.

Binary file not shown.

View File

@ -259,7 +259,7 @@ function declare($, _, openerp) {
_.each(messages, function(message) { _.each(messages, function(message) {
if (! message.technical) { if (! message.technical) {
defs.push(self.activate_session(message.session_id[0]).then(function(conv) { defs.push(self.activate_session(message.session_id[0]).then(function(conv) {
received = true; received = self.my_id !== message.from_id[0];
return conv.received_message(message); return conv.received_message(message);
})); }));
} else { } else {
@ -268,12 +268,13 @@ function declare($, _, openerp) {
defs.push($.when(im_common.technical_messages_handlers[json.type](self, message))); defs.push($.when(im_common.technical_messages_handlers[json.type](self, message)));
} }
}); });
if (! this.get("window_focus") && received) { return $.when.apply($, defs).then(function(){
this.set("waiting_messages", this.get("waiting_messages") + messages.length); if (! self.get("window_focus") && received) {
this.ting.play(); self.set("waiting_messages", self.get("waiting_messages") + messages.length);
this.create_ting(); self.ting.play();
} self.create_ting();
return $.when.apply($, defs); }
});
}, },
calc_positions: function() { calc_positions: function() {
var current = this.get("right_offset"); var current = this.get("right_offset");
@ -520,7 +521,7 @@ function declare($, _, openerp) {
txt += _.escape(str.slice(last, result.index)); txt += _.escape(str.slice(last, result.index));
last = url_regex.lastIndex; last = url_regex.lastIndex;
var url = _.escape(result[0]); var url = _.escape(result[0]);
txt += '<a href="' + url + '">' + url + '</a>'; txt += '<a href="' + url + '" target="_blank">' + url + '</a>';
} }
txt += _.escape(str.slice(last, str.length)); txt += _.escape(str.slice(last, str.length));
return txt; return txt;

View File

@ -252,10 +252,9 @@ class mail_thread(osv.AbstractModel):
new = set(command[2]) new = set(command[2])
# remove partners that are no longer followers # remove partners that are no longer followers
self.message_unsubscribe(cr, uid, [id], list(old-new)) self.message_unsubscribe(cr, uid, [id], list(old-new), context=context)
# add new followers # add new followers
self.message_subscribe(cr, uid, [id], list(new-old)) self.message_subscribe(cr, uid, [id], list(new-old), context=context)
def _search_followers(self, cr, uid, obj, name, args, context): def _search_followers(self, cr, uid, obj, name, args, context):
"""Search function for message_follower_ids """Search function for message_follower_ids
@ -291,7 +290,7 @@ class mail_thread(osv.AbstractModel):
'message_is_follower': fields.function(_get_followers, type='boolean', 'message_is_follower': fields.function(_get_followers, type='boolean',
fnct_search=_search_is_follower, string='Is a Follower', multi='_get_followers,'), fnct_search=_search_is_follower, string='Is a Follower', multi='_get_followers,'),
'message_follower_ids': fields.function(_get_followers, fnct_inv=_set_followers, 'message_follower_ids': fields.function(_get_followers, fnct_inv=_set_followers,
fnct_search=_search_followers, type='many2many', fnct_search=_search_followers, type='many2many', priority=-10,
obj='res.partner', string='Followers', multi='_get_followers'), obj='res.partner', string='Followers', multi='_get_followers'),
'message_ids': fields.one2many('mail.message', 'res_id', 'message_ids': fields.one2many('mail.message', 'res_id',
domain=lambda self: [('model', '=', self._name)], domain=lambda self: [('model', '=', self._name)],
@ -344,16 +343,22 @@ class mail_thread(osv.AbstractModel):
if context is None: if context is None:
context = {} context = {}
thread_id = super(mail_thread, self).create(cr, uid, values, context=context) # subscribe uid unless asked not to
if not context.get('mail_create_nosubscribe'):
pid = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid).partner_id.id
message_follower_ids = values.get('message_follower_ids') or [] # webclient can send None or False
message_follower_ids.append([4, pid])
values['message_follower_ids'] = message_follower_ids
# add operation to ignore access rule checking for subscription
context_operation = dict(context, operation='create')
else:
context_operation = context
thread_id = super(mail_thread, self).create(cr, uid, values, context=context_operation)
# automatic logging unless asked not to (mainly for various testing purpose) # automatic logging unless asked not to (mainly for various testing purpose)
if not context.get('mail_create_nolog'): if not context.get('mail_create_nolog'):
self.message_post(cr, uid, thread_id, body=_('%s created') % (self._description), context=context) self.message_post(cr, uid, thread_id, body=_('%s created') % (self._description), context=context)
# subscribe uid unless asked not to
if not context.get('mail_create_nosubscribe'):
self.message_subscribe_users(cr, uid, [thread_id], [uid], context=context)
# auto_subscribe: take values and defaults into account # auto_subscribe: take values and defaults into account
create_values = dict(values) create_values = dict(values)
for key, val in context.iteritems(): for key, val in context.iteritems():
@ -1115,11 +1120,23 @@ class mail_thread(osv.AbstractModel):
alternative = True alternative = True
if part.get_content_maintype() == 'multipart': if part.get_content_maintype() == 'multipart':
continue # skip container continue # skip container
filename = part.get_filename() # None if normal part # part.get_filename returns decoded value if able to decode, coded otherwise.
# original get_filename is not able to decode iso-8859-1 (for instance).
# therefore, iso encoded attachements are not able to be decoded properly with get_filename
# code here partially copy the original get_filename method, but handle more encoding
filename=part.get_param('filename', None, 'content-disposition')
if not filename:
filename=part.get_param('name', None)
if filename:
if isinstance(filename, tuple):
# RFC2231
filename=email.utils.collapse_rfc2231_value(filename).strip()
else:
filename=decode(filename)
encoding = part.get_content_charset() # None if attachment encoding = part.get_content_charset() # None if attachment
# 1) Explicit Attachments -> attachments # 1) Explicit Attachments -> attachments
if filename or part.get('content-disposition', '').strip().startswith('attachment'): if filename or part.get('content-disposition', '').strip().startswith('attachment'):
attachments.append((decode(filename) or 'attachment', part.get_payload(decode=True))) attachments.append((filename or 'attachment', part.get_payload(decode=True)))
continue continue
# 2) text/plain -> <pre/> # 2) text/plain -> <pre/>
if part.get_content_type() == 'text/plain' and (not alternative or not body): if part.get_content_type() == 'text/plain' and (not alternative or not body):
@ -1532,20 +1549,26 @@ class mail_thread(osv.AbstractModel):
def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None): def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None):
""" Add partners to the records followers. """ """ Add partners to the records followers. """
if context is None:
context = {}
mail_followers_obj = self.pool.get('mail.followers') mail_followers_obj = self.pool.get('mail.followers')
subtype_obj = self.pool.get('mail.message.subtype') subtype_obj = self.pool.get('mail.message.subtype')
user_pid = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id user_pid = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
if set(partner_ids) == set([user_pid]): if set(partner_ids) == set([user_pid]):
try: if context.get('operation', '') != 'create':
self.check_access_rights(cr, uid, 'read') try:
except (osv.except_osv, orm.except_orm): self.check_access_rights(cr, uid, 'read')
return self.check_access_rule(cr, uid, ids, 'read')
except (osv.except_osv, orm.except_orm):
return False
else: else:
self.check_access_rights(cr, uid, 'write') self.check_access_rights(cr, uid, 'write')
self.check_access_rule(cr, uid, ids, 'write')
existing_pids_dict = {} existing_pids_dict = {}
fol_ids = mail_followers_obj.search(cr, SUPERUSER_ID, [('res_model', '=', self._name), ('res_id', 'in', ids)]) fol_ids = mail_followers_obj.search(cr, SUPERUSER_ID, ['&', '&', ('res_model', '=', self._name), ('res_id', 'in', ids), ('partner_id', 'in', partner_ids)])
for fol in mail_followers_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context): for fol in mail_followers_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context):
existing_pids_dict.setdefault(fol.res_id, set()).add(fol.partner_id.id) existing_pids_dict.setdefault(fol.res_id, set()).add(fol.partner_id.id)
@ -1587,8 +1610,10 @@ class mail_thread(osv.AbstractModel):
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)['partner_id'][0]
if set(partner_ids) == set([user_pid]): if set(partner_ids) == set([user_pid]):
self.check_access_rights(cr, uid, 'read') self.check_access_rights(cr, uid, 'read')
self.check_access_rule(cr, uid, ids, 'read')
else: else:
self.check_access_rights(cr, uid, 'write') self.check_access_rights(cr, uid, 'write')
self.check_access_rule(cr, uid, ids, 'write')
fol_obj = self.pool['mail.followers'] fol_obj = self.pool['mail.followers']
fol_ids = fol_obj.search( fol_ids = fol_obj.search(
cr, SUPERUSER_ID, [ cr, SUPERUSER_ID, [

View File

@ -1438,7 +1438,7 @@ openerp.mail = function (session) {
message_fetch: function (replace_domain, replace_context, ids, callback) { message_fetch: function (replace_domain, replace_context, ids, callback) {
return this.ds_message.call('message_read', [ return this.ds_message.call('message_read', [
// ids force to read // ids force to read
ids == false ? undefined : ids, ids === false ? undefined : ids,
// domain + additional // domain + additional
(replace_domain ? replace_domain : this.domain), (replace_domain ? replace_domain : this.domain),
// ids allready loaded // ids allready loaded
@ -1814,7 +1814,7 @@ openerp.mail = function (session) {
if ('display_log_button' in this.options) { if ('display_log_button' in this.options) {
this.node.params.display_log_button = this.options.display_log_button; this.node.params.display_log_button = this.options.display_log_button;
} }
this.domain = this.node.params && this.node.params.domain || []; this.domain = (this.node.params && this.node.params.domain) || (this.field && this.field.domain) || [];
if (!this.ParentViewManager.is_action_enabled('edit')) { if (!this.ParentViewManager.is_action_enabled('edit')) {
this.node.params.show_link = false; this.node.params.show_link = false;

View File

@ -5,7 +5,7 @@
<!-- Top menu item --> <!-- Top menu item -->
<menuitem name="Marketing" <menuitem name="Marketing"
id="base.marketing_menu" id="base.marketing_menu"
groups="marketing.group_marketing_user,marketing.group_marketing_manager" groups="base.group_user"
sequence="85"/> sequence="85"/>
<record id="view_crm_lead_form" model="ir.ui.view"> <record id="view_crm_lead_form" model="ir.ui.view">

View File

@ -16,8 +16,6 @@
</data> </data>
<data> <data>
<menuitem name="Configuration" id="menu_marketing_configuration" parent="base.marketing_menu" sequence="1"/>
<!-- Marketing Campaign --> <!-- Marketing Campaign -->
<record id="act_marketing_campaing_segment_opened" model="ir.actions.act_window"> <record id="act_marketing_campaing_segment_opened" model="ir.actions.act_window">
@ -177,7 +175,7 @@
</field> </field>
</record> </record>
<menuitem name="Campaigns" id="menu_marketing_campaign" parent="base.marketing_menu"/> <menuitem name="Campaigns" id="menu_marketing_campaign" parent="base.marketing_menu" groups="marketing.group_marketing_user,marketing.group_marketing_manager"/>
<menuitem id="menu_marketing_campaign_form" parent="menu_marketing_campaign" action="action_marketing_campaign_form" sequence="30"/> <menuitem id="menu_marketing_campaign_form" parent="menu_marketing_campaign" action="action_marketing_campaign_form" sequence="30"/>
<!-- Marketing Segments --> <!-- Marketing Segments -->

View File

@ -358,7 +358,7 @@
</record> </record>
<!-- Top menu item --> <!-- Top menu item -->
<menuitem name="Marketing" id="base.marketing_menu" sequence="85"/> <menuitem name="Marketing" id="base.marketing_menu" sequence="85" groups="base.group_user"/>
<!-- Add in marketing --> <!-- Add in marketing -->
<menuitem name="Mass Mailing" id="mass_mailing_campaign" <menuitem name="Mass Mailing" id="mass_mailing_campaign"

View File

@ -1,6 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mass_mailing_campaign,mail.mass_mailing.campaign,model_mail_mass_mailing_campaign,,1,1,1,0 access_mass_mailing_campaign,mail.mass_mailing.campaign,model_mail_mass_mailing_campaign,base.group_user,1,1,1,0
access_mass_mailing_campaign_system,mail.mass_mailing.campaign.system,model_mail_mass_mailing_campaign,base.group_system,1,1,1,1 access_mass_mailing_campaign_system,mail.mass_mailing.campaign.system,model_mail_mass_mailing_campaign,base.group_system,1,1,1,1
access_mass_mailing,mail.mass_mailing,model_mail_mass_mailing,,1,1,1,0 access_mass_mailing,mail.mass_mailing,model_mail_mass_mailing,base.group_user,1,1,1,0
access_mass_mailing_system,mail.mass_mailing.system,model_mail_mass_mailing,base.group_system,1,1,1,1 access_mass_mailing_system,mail.mass_mailing.system,model_mail_mass_mailing,base.group_system,1,1,1,1
access_mail_mail_statistics,mail.mail.statistics,model_mail_mail_statistics,,1,1,1,1 access_mail_mail_statistics,mail.mail.statistics,model_mail_mail_statistics,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_mass_mailing_campaign mail.mass_mailing.campaign model_mail_mass_mailing_campaign base.group_user 1 1 1 0
3 access_mass_mailing_campaign_system mail.mass_mailing.campaign.system model_mail_mass_mailing_campaign base.group_system 1 1 1 1
4 access_mass_mailing mail.mass_mailing model_mail_mass_mailing base.group_user 1 1 1 0
5 access_mass_mailing_system mail.mass_mailing.system model_mail_mass_mailing base.group_system 1 1 1 1
6 access_mail_mail_statistics mail.mail.statistics model_mail_mail_statistics base.group_user 1 1 1 1

View File

@ -43,15 +43,18 @@ class MailComposeMessage(osv.TransientModel):
email mass mailing. """ email mass mailing. """
res = super(MailComposeMessage, self).get_mail_values(cr, uid, wizard, res_ids, context=context) res = super(MailComposeMessage, self).get_mail_values(cr, uid, wizard, res_ids, context=context)
if wizard.composition_mode == 'mass_mail' and wizard.mass_mailing_campaign_id: # TODO: which kind of mass mailing ? if wizard.composition_mode == 'mass_mail' and wizard.mass_mailing_campaign_id: # TODO: which kind of mass mailing ?
current_date = fields.datetime.now() if wizard.mass_mailing_id:
mass_mailing_id = self.pool['mail.mass_mailing'].create( mass_mailing_id = wizard.mass_mailing_id.id
cr, uid, { else:
'mass_mailing_campaign_id': wizard.mass_mailing_campaign_id.id, current_date = fields.datetime.now()
'name': '%s-%s' % (wizard.mass_mailing_campaign_id.name, current_date), mass_mailing_id = self.pool['mail.mass_mailing'].create(
'date': current_date, cr, uid, {
'domain': wizard.active_domain, 'mass_mailing_campaign_id': wizard.mass_mailing_campaign_id.id,
'template_id': wizard.template_id and wizard.template_id.id or False, 'name': '%s-%s' % (wizard.mass_mailing_campaign_id.name, current_date),
}, context=context) 'date': current_date,
'domain': wizard.active_domain,
'template_id': wizard.template_id and wizard.template_id.id or False,
}, context=context)
for res_id in res_ids: for res_id in res_ids:
res[res_id]['statistics_ids'] = [(0, 0, { res[res_id]['statistics_ids'] = [(0, 0, {
'model': wizard.model, 'model': wizard.model,

View File

@ -33,11 +33,6 @@ class procurement_order(osv.osv):
'production_id': fields.many2one('mrp.production', 'Manufacturing Order'), 'production_id': fields.many2one('mrp.production', 'Manufacturing Order'),
} }
def _prepare_order_line_procurement(self, cr, uid, order, line, move_id, date_planned, context=None):
result = super(procurement_order, self)._prepare_order_line_procurement(cr, uid, order, line, move_id, date_planned, context)
result['property_ids'] = [(6, 0, [x.id for x in line.property_ids])]
return result
def check_produce_product(self, cr, uid, procurement, context=None): def check_produce_product(self, cr, uid, procurement, context=None):
''' Depict the capacity of the procurement workflow to produce products (not services)''' ''' Depict the capacity of the procurement workflow to produce products (not services)'''
return True return True
@ -126,6 +121,3 @@ class procurement_order(osv.osv):
for procurement in self.browse(cr, uid, ids, context=context): for procurement in self.browse(cr, uid, ids, context=context):
body = _("Manufacturing Order <em>%s</em> created.") % ( procurement.production_id.name,) body = _("Manufacturing Order <em>%s</em> created.") % ( procurement.production_id.name,)
self.message_post(cr, uid, [procurement.id], body=body, context=context) self.message_post(cr, uid, [procurement.id], body=body, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -23,7 +23,7 @@ import logging
from openerp.osv import fields, osv from openerp.osv import fields, osv
from openerp.tools.translate import _ from openerp.tools.translate import _
from openerp.tools import email_re from openerp.tools import email_split
from openerp import SUPERUSER_ID from openerp import SUPERUSER_ID
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -53,8 +53,8 @@ http://www.openerp.com
def extract_email(email): def extract_email(email):
""" extract the email address from a user-friendly email address """ """ extract the email address from a user-friendly email address """
m = email_re.search(email or "") addresses = email_split(email)
return m and m.group(0) or "" return addresses[0] if addresses else ''

View File

@ -51,6 +51,12 @@ class TestPortalProjectBase(TestProjectBase):
'alias_name': 'donovan', 'alias_name': 'donovan',
'groups_id': [(6, 0, [self.group_anonymous_id])] 'groups_id': [(6, 0, [self.group_anonymous_id])]
}) })
self.user_manager_id = self.res_users.create(cr, uid, {
'name': 'Eustache Manager',
'login': 'eustache',
'alias_name': 'eustache',
'groups_id': [(6, 0, [self.group_project_manager_id])]
})
# Test 'Pigs' project # Test 'Pigs' project
self.project_pigs_id = self.project_project.create(cr, uid, { self.project_pigs_id = self.project_project.create(cr, uid, {
@ -220,7 +226,7 @@ class TestPortalProject(TestPortalProjectBase):
# Data: subscribe Alfred, Chell and Donovan as follower # Data: subscribe Alfred, Chell and Donovan as follower
self.project_project.message_subscribe_users(cr, uid, [pigs_id], [self.user_projectuser_id, self.user_portal_id, self.user_anonymous_id]) self.project_project.message_subscribe_users(cr, uid, [pigs_id], [self.user_projectuser_id, self.user_portal_id, self.user_anonymous_id])
self.project_task.message_subscribe_users(cr, self.user_projectuser_id, [self.task_1_id, self.task_3_id], [self.user_portal_id, self.user_projectuser_id]) 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) # 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, ['name'])

View File

@ -159,7 +159,7 @@ class TestPortalIssue(TestPortalProjectBase):
# Data: subscribe Alfred, Chell and Donovan as follower # Data: subscribe Alfred, Chell and Donovan as follower
self.project_project.message_subscribe_users(cr, uid, [pigs_id], [self.user_projectuser_id, self.user_portal_id, self.user_anonymous_id]) self.project_project.message_subscribe_users(cr, uid, [pigs_id], [self.user_projectuser_id, self.user_portal_id, self.user_anonymous_id])
self.project_issue.message_subscribe_users(cr, self.user_projectuser_id, [self.issue_1_id, self.issue_3_id], [self.user_portal_id, self.user_projectuser_id]) self.project_issue.message_subscribe_users(cr, self.user_manager_id, [self.issue_1_id, self.issue_3_id], [self.user_portal_id, self.user_projectuser_id])
# Do: Alfred reads project -> ok (follower ok followers) # Do: Alfred reads project -> ok (follower ok followers)
# Test: followed + assigned issues visible # Test: followed + assigned issues visible

View File

@ -46,9 +46,15 @@ class project_task_type(osv.osv):
'there are no records in that stage to display.'), 'there are no records in that stage to display.'),
} }
def _get_default_project_ids(self, cr, uid, ctx={}):
project_id = self.pool['project.task']._get_default_project_id(cr, uid, context=ctx)
if project_id:
return [project_id]
return None
_defaults = { _defaults = {
'sequence': 1, 'sequence': 1,
'project_ids': lambda self, cr, uid, ctx=None: self.pool['project.task']._get_default_project_id(cr, uid, context=ctx), 'project_ids': _get_default_project_ids,
} }
_order = 'sequence' _order = 'sequence'
@ -64,7 +70,7 @@ class project(osv.osv):
""" Installation hook: aliases, project.project """ """ Installation hook: aliases, project.project """
# create aliases for all projects and avoid constraint errors # create aliases for all projects and avoid constraint errors
alias_context = dict(context, alias_model_name='project.task') alias_context = dict(context, alias_model_name='project.task')
self.pool.get('mail.alias').migrate_to_alias(cr, self._name, self._table, super(project, self)._auto_init, return self.pool.get('mail.alias').migrate_to_alias(cr, self._name, self._table, super(project, self)._auto_init,
'project.task', self._columns['alias_id'], 'id', alias_prefix='project+', alias_defaults={'project_id':'id'}, context=alias_context) 'project.task', self._columns['alias_id'], 'id', alias_prefix='project+', alias_defaults={'project_id':'id'}, context=alias_context)
def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):

View File

@ -21,7 +21,7 @@
import time import time
import pytz import pytz
from openerp import SUPERUSER_ID from openerp import SUPERUSER_ID, workflow
from datetime import datetime from datetime import datetime
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from operator import attrgetter from operator import attrgetter
@ -245,7 +245,7 @@ class purchase_order(osv.osv):
_name = "purchase.order" _name = "purchase.order"
_inherit = ['mail.thread', 'ir.needaction_mixin'] _inherit = ['mail.thread', 'ir.needaction_mixin']
_description = "Purchase Order" _description = "Purchase Order"
_order = "name desc" _order = 'date_order desc, id desc'
def create(self, cr, uid, vals, context=None): def create(self, cr, uid, vals, context=None):
if vals.get('name','/')=='/': if vals.get('name','/')=='/':
@ -1290,6 +1290,7 @@ class account_invoice(osv.Model):
po_ids = purchase_order_obj.search(cr, user_id, [('invoice_ids', 'in', ids)], context=context) po_ids = purchase_order_obj.search(cr, user_id, [('invoice_ids', 'in', ids)], context=context)
for po_id in po_ids: for po_id in po_ids:
purchase_order_obj.message_post(cr, user_id, po_id, body=_("Invoice received"), context=context) purchase_order_obj.message_post(cr, user_id, po_id, body=_("Invoice received"), context=context)
workflow.trg_write(uid, 'purchase.order', po_id, cr)
return res return res
def confirm_paid(self, cr, uid, ids, context=None): def confirm_paid(self, cr, uid, ids, context=None):

View File

@ -147,7 +147,7 @@
<record id="trans_router_invoice_no_order" model="workflow.transition"> <record id="trans_router_invoice_no_order" model="workflow.transition">
<field name="act_from" ref="act_router"/> <field name="act_from" ref="act_router"/>
<field name="act_to" ref="act_invoice_end"/> <field name="act_to" ref="act_invoice_end"/>
<field name="condition">invoice_method&lt;&gt;'order' and invoiced</field> <field name="condition">invoice_method&lt;&gt;'order'</field>
</record> </record>
<record id="trans_except_picking_picking_done" model="workflow.transition"> <record id="trans_except_picking_picking_done" model="workflow.transition">
<field name="act_from" ref="act_except_picking"/> <field name="act_from" ref="act_except_picking"/>
@ -200,6 +200,7 @@
<record id="trans_invoice_end_done" model="workflow.transition"> <record id="trans_invoice_end_done" model="workflow.transition">
<field name="act_from" ref="act_invoice_end"/> <field name="act_from" ref="act_invoice_end"/>
<field name="act_to" ref="act_done"/> <field name="act_to" ref="act_done"/>
<field name="condition">invoiced</field>
</record> </record>
<!-- Procurement --> <!-- Procurement -->

View File

@ -238,7 +238,7 @@ class sale_order(osv.osv):
_sql_constraints = [ _sql_constraints = [
('name_uniq', 'unique(name, company_id)', 'Order Reference must be unique per Company!'), ('name_uniq', 'unique(name, company_id)', 'Order Reference must be unique per Company!'),
] ]
_order = 'name desc' _order = 'date_order desc, id desc'
# Form filling # Form filling
def unlink(self, cr, uid, ids, context=None): def unlink(self, cr, uid, ids, context=None):

View File

@ -88,4 +88,10 @@ class mrp_production(osv.osv):
} }
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: class sale_order(osv.Model):
_inherit ='sale.order'
def _prepare_order_line_procurement(self, cr, uid, order, line, move_id, date_planned, context=None):
result = super(sale_order, self)._prepare_order_line_procurement(cr, uid, order, line, move_id, date_planned, context)
result['property_ids'] = [(6, 0, [x.id for x in line.property_ids])]
return result

View File

@ -124,10 +124,22 @@ class stock_picking(osv.osv):
def _invoice_hook(self, cursor, user, picking, invoice_id): def _invoice_hook(self, cursor, user, picking, invoice_id):
sale_obj = self.pool.get('sale.order') sale_obj = self.pool.get('sale.order')
order_line_obj = self.pool.get('sale.order.line')
invoice_obj = self.pool.get('account.invoice')
invoice_line_obj = self.pool.get('account.invoice.line')
if picking.sale_id: if picking.sale_id:
sale_obj.write(cursor, user, [picking.sale_id.id], { sale_obj.write(cursor, user, [picking.sale_id.id], {
'invoice_ids': [(4, invoice_id)], 'invoice_ids': [(4, invoice_id)],
}) })
for sale_line in picking.sale_id.order_line:
if sale_line.product_id.type == 'service' and not sale_line.invoiced:
vals = order_line_obj._prepare_order_line_invoice_line(cursor, user, sale_line, False)
vals['invoice_id'] = invoice_id
invoice_line_id = invoice_line_obj.create(cursor, user, vals)
order_line_obj.write(cursor, user, [sale_line.id], {
'invoice_lines': [(6, 0, [invoice_line_id])],
})
invoice_obj.button_compute(cursor, user, [invoice_id])
return super(stock_picking, self)._invoice_hook(cursor, user, picking, invoice_id) return super(stock_picking, self)._invoice_hook(cursor, user, picking, invoice_id)
def action_done(self, cr, uid, ids, context=None): def action_done(self, cr, uid, ids, context=None):

View File

@ -3,6 +3,16 @@
- -
!context !context
uid: 'res_sale_stock_salesman' uid: 'res_sale_stock_salesman'
-
Add SO line with service type product in SO to check flow which contain service type product in SO(BUG#1167330).
-
!record {model: sale.order.line, id: sale_order_1}:
name: 'On Site Assistance'
product_id: product.product_product_2
product_uom_qty: 1.0
product_uom: 1
price_unit: 150.0
order_id: sale.sale_order_6
- -
First I check the total amount of the Quotation before Approved. First I check the total amount of the Quotation before Approved.
- -
@ -75,7 +85,7 @@
assert picking.partner_id.id == sale_order.partner_shipping_id.id,"Shipping Address is not correspond with sale order." assert picking.partner_id.id == sale_order.partner_shipping_id.id,"Shipping Address is not correspond with sale order."
assert picking.note == sale_order.note,"Note is not correspond with sale order." assert picking.note == sale_order.note,"Note is not correspond with sale order."
assert picking.invoice_state == (sale_order.order_policy=='picking' and '2binvoiced') or 'none',"Invoice policy is not correspond with sale order." assert picking.invoice_state == (sale_order.order_policy=='picking' and '2binvoiced') or 'none',"Invoice policy is not correspond with sale order."
assert len(picking.move_lines) == len(sale_order.order_line), "Total move of delivery order are not corresposning with total sale order lines." assert len(picking.move_lines) == len(sale_order.order_line) - 1, "Total move of delivery order are not corresposning with total sale order lines."
location_id = sale_order.warehouse_id.lot_stock_id.id location_id = sale_order.warehouse_id.lot_stock_id.id
output_id = sale_order.warehouse_id.lot_output_id.id output_id = sale_order.warehouse_id.lot_output_id.id
for move in picking.move_lines: for move in picking.move_lines:

View File

@ -144,7 +144,7 @@ class stock_partial_picking(osv.osv_memory):
def _partial_move_for(self, cr, uid, move): def _partial_move_for(self, cr, uid, move):
partial_move = { partial_move = {
'product_id' : move.product_id.id, 'product_id' : move.product_id.id,
'quantity' : move.product_qty if move.state == 'assigned' else 0, 'quantity' : move.product_qty if move.state == 'assigned' or move.picking_id.type == 'in' else 0,
'product_uom' : move.product_uom.id, 'product_uom' : move.product_uom.id,
'prodlot_id' : move.prodlot_id.id, 'prodlot_id' : move.prodlot_id.id,
'move_id' : move.id, 'move_id' : move.id,