[MERGE] Forward port of 7.0 until rev. 8534.

bzr revid: vmt@openerp.com-20130104093953-p92uylaux9u24isn
This commit is contained in:
Vo Minh Thu 2013-01-04 10:39:53 +01:00
commit 5e8f418d41
42 changed files with 330 additions and 210 deletions

View File

@ -28,8 +28,7 @@
<field name="nbr" sum="# of Lines"/>
<field name="product_qty" sum="Qty"/>
<!-- <field name="reconciled" sum="# Reconciled"/> -->
<field name="user_currency_price_total" sum="Total Without Tax"/>
<field name="user_currency_residual" sum="Total Residual" invisible="context.get('residual_invisible',False)"/>
<field name="price_total" sum="Total Without Tax"/>
</tree>
</field>
</record>

View File

@ -48,7 +48,7 @@
</record>
<record id="account_asset_asset_cab0" model="account.asset.asset">
<field name="method_end">2014-08-11</field>
<field name="method_end" eval="(DateTime.now().replace(month=8, day=11) + timedelta(days=3*365)).strftime('%Y-%m-%d')"/>
<field eval="0.0" name="salvage_value"/>
<field name="method_time">end</field>
<field name="name">V6 Engine and 10 inches tires</field>
@ -63,7 +63,7 @@
<field name="state">open</field>
<field eval="12" name="method_period"/>
<field eval="20" name="method_number"/>
<field name="purchase_date">2012-01-01</field>
<field name="purchase_date" eval="time.strftime('%Y-01-01')"/>
<field name="name">Office</field>
<field eval="500000.0" name="purchase_value"/>
<field name="category_id" ref="account_asset_category_fixedassets0"/>

View File

@ -280,19 +280,29 @@ class res_partner(osv.osv):
def do_button_print(self, cr, uid, ids, context=None):
assert(len(ids) == 1)
company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
#search if the partner has accounting entries to print. If not, it may not be present in the
#psql view the report is based on, so we need to stop the user here.
if not self.pool.get('account.move.line').search(cr, uid, [
('partner_id', '=', ids[0]),
('account_id.type', '=', 'receivable'),
('reconcile_id', '=', False),
('state', '!=', 'draft'),
('company_id', '=', company_id),
], context=context):
raise osv.except_osv(_('Error!'),_("The partner does not have any accounting entries to print in the overdue report for the current company."))
self.message_post(cr, uid, [ids[0]], body=_('Printed overdue payments report'), context=context)
datas = {
'ids': ids,
'model': 'res.partner',
'form': self.read(cr, uid, ids[0], context=context)
#build the id of this partner in the psql view. Could be replaced by a search with [('company_id', '=', company_id),('partner_id', '=', ids[0])]
wizard_partner_ids = [ids[0] * 10000 + company_id]
followup_ids = self.pool.get('account_followup.followup').search(cr, uid, [('company_id', '=', company_id)], context=context)
if not followup_ids:
raise osv.except_osv(_('Error!'),_("There is no followup plan defined for the current company."))
data = {
'date': fields.date.today(),
'followup_id': followup_ids[0],
}
return {
'type': 'ir.actions.report.xml',
'report_name': 'account.overdue',
'datas': datas,
'nodestroy' : True
}
#call the print overdue report on this partner
return self.do_partner_print(cr, uid, wizard_partner_ids, data, context=context)
def _get_amounts_and_date(self, cr, uid, ids, name, arg, context=None):
'''

View File

@ -123,24 +123,10 @@
</p>
</field>
</record>
<record id="action_account_manual_reconcile_receivable" model="ir.actions.act_window">
<field name="name">Reconcile Invoices &amp; Payments</field>
<field name="search_view_id" ref="view_account_followup_filter"/>
<field name="context">{'search_default_unreconciled': 1,'view_mode':True}</field>
<field name="domain">[('account_id.type', '=', 'receivable')]</field>
<field name="res_model">account.move.line</field>
<field name="view_id" ref="account.view_move_line_tree_reconcile"/>
<field name="view_mode">tree_account_reconciliation</field>
<field name="help" type="html">
<p>
No journal items found.
</p>
</field>
</record>
<menuitem
name="Reconcile Invoices &amp; Payments"
action="action_account_manual_reconcile_receivable"
action="account.action_account_manual_reconcile"
parent="menu_finance_followup"
sequence="0"
id="menu_manual_reconcile_followup"/>

View File

@ -79,24 +79,29 @@ class report_rappel(report_sxw.rml_parse):
def _get_text(self, stat_line, followup_id, context=None):
if context is None:
context = {}
context.update({'lang': stat_line.partner_id.lang})
fp_obj = pooler.get_pool(self.cr.dbname).get('account_followup.followup')
fp_line = fp_obj.browse(self.cr, self.uid, followup_id).followup_line
fp_line = fp_obj.browse(self.cr, self.uid, followup_id, context=context).followup_line
if not fp_line:
raise osv.except_osv(_('Error!'),_("The followup plan defined for the current company does not have any followup action."))
#the default text will be the first fp_line in the sequence with a description.
default_text = ''
li_delay = []
for line in fp_line:
if not default_text and line.description:
default_text = line.description
li_delay.append(line.delay)
li_delay.sort(reverse=True)
text = ""
a = {}
partner_line_ids = pooler.get_pool(self.cr.dbname).get('account.move.line').search(self.cr, self.uid, [('partner_id','=',stat_line.partner_id.id),('reconcile_id','=',False),('company_id','=',stat_line.company_id.id),('blocked','=',False)])
partner_delay = []
context.update({'lang': stat_line.partner_id.lang})
for i in pooler.get_pool(self.cr.dbname).get('account.move.line').browse(self.cr, self.uid, partner_line_ids, context):
for delay in li_delay:
if i.followup_line_id and str(i.followup_line_id.delay)==str(delay):
text = i.followup_line_id.description
a[delay] = text
partner_delay.append(delay)
text = partner_delay and a[max(partner_delay)] or ''
#look into the lines of the partner that already have a followup level, and take the description of the higher level for which it is available
partner_line_ids = pooler.get_pool(self.cr.dbname).get('account.move.line').search(self.cr, self.uid, [('partner_id','=',stat_line.partner_id.id),('reconcile_id','=',False),('company_id','=',stat_line.company_id.id),('blocked','=',False),('state','!=','draft'),('debit','!=',False),('account_id.type','=','receivable'),('followup_line_id','!=',False)])
partner_max_delay = 0
partner_max_text = ''
for i in pooler.get_pool(self.cr.dbname).get('account.move.line').browse(self.cr, self.uid, partner_line_ids, context=context):
if i.followup_line_id.delay > partner_max_delay and i.followup_line_id.description:
partner_max_delay = i.followup_line_id.delay
partner_max_text = i.followup_line_id.description
text = partner_max_delay and partner_max_text or default_text
if text:
text = text % {
'partner_name': stat_line.partner_id.name,

View File

@ -3,7 +3,7 @@
-
!record {model: account.invoice, id: account.demo_invoice_0}:
check_total: 14.0
date_invoice: 2012-06-2
date_invoice: !eval "'%s-06-2' %(datetime.now().year)"
invoice_line:
- account_id : account.a_sale
name: 'Test PC'

View File

@ -17,7 +17,7 @@
<record id="mt_voucher_state_change" model="mail.message.subtype">
<field name="name">Status Change</field>
<field name="res_model">account.voucher</field>
<field name="description">Status &lt;b&gt;changed&lt;/b&gt;</field>
<field name="description">Status changed</field>
</record>
</data>

View File

@ -6,17 +6,17 @@
<record id="mt_account_pending" model="mail.message.subtype">
<field name="name">Contract to Renew</field>
<field name="res_model">account.analytic.account</field>
<field name="description">Contract &lt;b&gt;pending&lt;/b&gt;</field>
<field name="description">Contract pending</field>
</record>
<record id="mt_account_closed" model="mail.message.subtype">
<field name="name">Contract Finished</field>
<field name="res_model">account.analytic.account</field>
<field name="description">Contract &lt;b&gt;closed&lt;/b&gt;</field>
<field name="description">Contract closed</field>
</record>
<record id="mt_account_opened" model="mail.message.subtype">
<field name="name">Contract Opened</field>
<field name="res_model">account.analytic.account</field>
<field name="description">Stage &lt;b&gt;opened&lt;/b&gt;</field>
<field name="description">Contract opened</field>
</record>
</data>

View File

@ -46,7 +46,18 @@ oidutil.log = _logger.debug
def get_system_user():
"""Return system user info string, such as USERNAME-EUID"""
info = getpass.getuser()
try:
info = getpass.getuser()
except ImportError:
if os.name == 'nt':
# when there is no 'USERNAME' in environment, getpass.getuser()
# fail when trying to import 'pwd' module - which is unix only.
# In that case we have to fallback to real win32 API.
import win32api
info = win32api.GetUserName()
else:
raise
euid = getattr(os, 'geteuid', None) # Non available on some platforms
if euid is not None:
info = '%s-%d' % (info, euid())

View File

@ -252,12 +252,19 @@ class res_users(osv.Model):
raise osv.except_osv(_("Cannot send email: user has no email address."), user.name)
mail_id = self.pool.get('email.template').send_mail(cr, uid, template.id, user.id, True, context=context)
mail_state = mail_obj.read(cr, uid, mail_id, ['state'], context=context)
if mail_state and mail_state == 'exception':
if mail_state and mail_state['state'] == 'exception':
raise osv.except_osv(_("Cannot send email: no outgoing email server configured.\nYou can configure it under Settings/General Settings."), user.name)
else:
raise osv.except_osv(_("Mail sent to:"), user.email)
return True
return {
'type': 'ir.actions.client',
'name': '_(Server Notification)',
'tag': 'action_notify',
'params': {
'title': 'Mail Sent to: %s' % user.name,
'text': 'You can reset the password by yourself using this <a href=%s>link</a>' % user.partner_id.signup_url,
'sticky': True,
}
}
def create(self, cr, uid, values, context=None):
# overridden to automatically invite user to sign up

View File

@ -142,7 +142,7 @@ html_invitation = """
<td width="100%%">You are invited for <i>%(company)s</i> Event.</td>
</tr>
<tr>
<td width="100%%">Below are the details of event:</td>
<td width="100%%">Below are the details of event. Hours and dates expressed in %(timezone)s time.</td>
</tr>
</table>
@ -427,12 +427,9 @@ property or property parameter."),
res = None
def ics_datetime(idate, short=False):
if idate:
if short or len(idate)<=10:
return date.fromtimestamp(time.mktime(time.strptime(idate, '%Y-%m-%d')))
else:
return datetime.strptime(idate, '%Y-%m-%d %H:%M:%S')
else:
return False
#returns the datetime as UTC, because it is stored as it in the database
return datetime.strptime(idate, '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.timezone('UTC'))
return False
try:
# FIXME: why isn't this in CalDAV?
import vobject
@ -516,9 +513,17 @@ property or property parameter."),
att_infos.append(((att2.user_id and att2.user_id.name) or \
(att2.partner_id and att2.partner_id.name) or \
att2.email) + ' - Status: ' + att2.state.title())
#dates and times are gonna be expressed in `tz` time (local timezone of the `uid`)
tz = context.get('tz', pytz.timezone('UTC'))
#res_obj.date and res_obj.date_deadline are in UTC in database so we use context_timestamp() to transform them in the `tz` timezone
date_start = fields.datetime.context_timestamp(cr, uid, datetime.strptime(res_obj.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context)
date_stop = False
if res_obj.date_deadline:
date_stop = fields.datetime.context_timestamp(cr, uid, datetime.strptime(res_obj.date_deadline, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context)
body_vals = {'name': res_obj.name,
'start_date': res_obj.date,
'end_date': res_obj.date_deadline or False,
'start_date': date_start,
'end_date': date_stop,
'timezone': tz,
'description': res_obj.description or '-',
'location': res_obj.location or '-',
'attendees': '<br>'.join(att_infos),
@ -623,7 +628,7 @@ property or property parameter."),
email = filter(lambda x:x.__contains__('@'), cnval)
vals['email'] = email and email[0] or ''
vals['cn'] = vals.get("cn")
res = super(calendar_attendee, self).create(cr, uid, vals, context)
res = super(calendar_attendee, self).create(cr, uid, vals, context=context)
return res
calendar_attendee()
@ -842,7 +847,7 @@ class calendar_alarm(osv.osv):
current_datetime = datetime.now()
alarm_ids = self.search(cr, uid, [('state', '!=', 'done')], context=context)
mail_to = []
mail_to = ""
for alarm in self.browse(cr, uid, alarm_ids, context=context):
next_trigger_date = None
@ -891,9 +896,9 @@ From:
</pre>
""" % (alarm.name, alarm.trigger_date, alarm.description, \
alarm.user_id.name, alarm.user_id.signature)
mail_to = [alarm.user_id.email]
mail_to = alarm.user_id.email
for att in alarm.attendee_ids:
mail_to.append(att.user_id.email)
mail_to = mail_to + " " + att.user_id.email
if mail_to:
vals = {
'state': 'outgoing',
@ -1117,7 +1122,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
for att in event.attendee_ids:
attendees[att.partner_id.id] = True
new_attendees = []
mail_to = []
mail_to = ""
for partner in event.partner_ids:
if partner.id in attendees:
continue
@ -1128,7 +1133,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
'email': partner.email
}, context=context)
if partner.email:
mail_to.append(partner.email)
mail_to = mail_to + " " + partner.email
self.write(cr, uid, [event.id], {
'attendee_ids': [(4, att_id)]
}, context=context)
@ -1136,7 +1141,7 @@ rule or repeating pattern of time to exclude from the recurring rule."),
if mail_to and current_user.email:
att_obj._send_mail(cr, uid, new_attendees, mail_to,
email_from = current_user.email)
email_from = current_user.email, context=context)
return True
def default_organizer(self, cr, uid, context=None):
@ -1747,57 +1752,4 @@ class virtual_report_spool(web_services.report_spool):
virtual_report_spool()
class res_users(osv.osv):
_inherit = 'res.users'
def _get_user_avail(self, cr, uid, ids, context=None):
"""
Get User Availability
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current user's ID for security checks,
@param ids: List of res user's IDs.
@param context: A standard dictionary for contextual values
"""
current_datetime = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
res = {}
attendee_obj = self.pool.get('calendar.attendee')
attendee_ids = attendee_obj.search(cr, uid, [
('event_date', '<=', current_datetime), ('event_end_date', '<=', current_datetime),
('state', '=', 'accepted'), ('user_id', 'in', ids)
])
for attendee_data in attendee_obj.read(cr, uid, attendee_ids, ['user_id']):
user_id = attendee_data['user_id']
status = 'busy'
res.update({user_id:status})
#TOCHECK: Delegated Event
for user_id in ids:
if user_id not in res:
res[user_id] = 'free'
return res
def _get_user_avail_fun(self, cr, uid, ids, name, args, context=None):
"""
Get User Availability Function
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current user's ID for security checks,
@param ids: List of res user's IDs.
@param context: A standard dictionary for contextual values
"""
return self._get_user_avail(cr, uid, ids, context=context)
_columns = {
'availability': fields.function(_get_user_avail_fun, type='selection', \
selection=[('free', 'Free'), ('busy', 'Busy')], \
string='Free/Busy'),
}
res_users()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -181,10 +181,6 @@
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_ids" widget="mail_thread" placeholder="Share a message..."/>
</div>
</form>
</field>
</record>

View File

@ -84,9 +84,20 @@ class crm_lead(base_stage, format_address, osv.osv):
},
}
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
if not vals.get('stage_id') and vals.get('section_id'):
ctx = context.copy()
ctx['default_section_id'] = vals['section_id']
vals['stage_id'] = self._get_default_stage_id(cr, uid, context=ctx)
elif not vals.get('stage_id') and context.get('default_section_id'):
vals['stage_id'] = self._get_default_stage_id(cr, uid, context=context)
return super(crm_lead, self).create(cr, uid, vals, context=context)
def _get_default_section_id(self, cr, uid, context=None):
""" Gives default section by checking if present in the context """
return (self._resolve_section_id_from_context(cr, uid, context=context) or False)
return self._resolve_section_id_from_context(cr, uid, context=context) or False
def _get_default_stage_id(self, cr, uid, context=None):
""" Gives default stage_id """
@ -671,9 +682,9 @@ class crm_lead(base_stage, format_address, osv.osv):
section_id = lead.section_id and lead.section_id.id or False
if section_id:
stage_ids = crm_stage.search(cr, uid, [('sequence','>=',1), ('section_ids','=', section_id)])
stage_ids = crm_stage.search(cr, uid, [('sequence', '>=', 1), ('section_ids', '=', section_id), ('probability', '>', 0)])
else:
stage_ids = crm_stage.search(cr, uid, [('sequence','>=',1)])
stage_ids = crm_stage.search(cr, uid, [('sequence', '>=', 1), ('probability', '>', 0)])
stage_id = stage_ids and stage_ids[0] or False
return {

View File

@ -402,9 +402,9 @@ Andrew</field>
<field name="contact_name">Leland Martinez</field>
<field name="email_from">info@deltapc.com</field>
<field name="partner_name">Delta PC</field>
<field name="city">Fremont</field>
<field name="city">London</field>
<field name="street">3661 Station Street</field>
<field name="country_id" ref="base.us"/>
<field name="country_id" ref="base.uk"/>
<field name="partner_id" ref="base.res_partner_4"/>
<field name="type_id" ref="type_lead8"/>
<field name="categ_ids" eval="[(6, 0, [categ_oppor4,categ_oppor6])]"/>

View File

@ -156,7 +156,7 @@
<label for="section_id"/>
<div>
<field name="section_id"/>
<button name="case_escalate" string="Escalate" type="object" states="draft,open,pending"/>
<button name="case_escalate" string="Escalate" type="object" attrs="{'invisible': ['|', ('section_id','=',False), ('state', 'not in', ['draft','open','pending'])]}"/>
</div>
<field name="type" invisible="1"/>
</group>
@ -421,8 +421,7 @@
<label for="section_id"/>
<div>
<field name="section_id" widget="selection"/>
<button name="case_escalate" string="Escalate" type="object" class="oe_link"
attrs="{'invisible': [('section_id', '=', False)]}"/>
<button name="case_escalate" string="Escalate" type="object" class="oe_link" attrs="{'invisible': ['|', ('section_id','=',False), ('state', 'not in', ['draft','open','pending'])]}"/>
</div>
</group>
<group>

View File

@ -36,11 +36,13 @@
<button string="Meeting"
states="open,pending"
name="action_make_meeting"
icon="gtk-redo"
type="object"/>
<button string="Convert to Opportunity"
type="object"
name="action_button_convert2opportunity"
states="open,pending"
icon="gtk-index"
attrs="{'invisible':[('opportunity_id','!=',False)]}"/>
<field name="partner_phone"/>
<field name="user_id"/>

View File

@ -117,8 +117,22 @@ class document_file(osv.osv):
# take partner from uid
if vals.get('res_id', False) and vals.get('res_model', False) and not vals.get('partner_id', False):
vals['partner_id'] = self.__get_partner_id(cr, uid, vals['res_model'], vals['res_id'], context)
if vals.get('datas', False):
vals['file_type'], vals['index_content'] = self._index(cr, uid, vals['datas'].decode('base64'), vals.get('datas_fname', False), None)
return super(document_file, self).create(cr, uid, vals, context)
def write(self, cr, uid, ids, vals, context=None):
if context is None:
context = {}
if vals.get('datas', False):
vals['file_type'], vals['index_content'] = self._index(cr, uid, vals['datas'].decode('base64'), vals.get('datas_fname', False), None)
return super(document_file, self).write(cr, uid, ids, vals, context)
def _index(self, cr, uid, data, datas_fname, file_type):
mime, icont = cntIndex.doIndex(data, datas_fname, file_type or None, None)
icont_u = ustr(icont)
return mime, icont_u
def __get_partner_id(self, cr, uid, res_model, res_id, context=None):
""" A helper to retrieve the associated partner from any res_model+id
It is a hack that will try to discover if the mentioned record is

View File

@ -167,7 +167,7 @@
<field name="inherit_id" ref="base.view_attachment_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='datas_fname']" position="replace">
<field name="datas_fname" invisible="1" on_change="onchange_file(datas_fname)"/>
<field name="datas_fname" invisible="1"/>
</xpath>
<field name="url" position="after">
<field name="user_id"/>

View File

@ -20,13 +20,20 @@
##############################################################################
import threading
import ftpserver
import logging
import authorizer
import abstracted_fs
import logging
import ftpserver
import openerp
from openerp.tools import config
_logger = logging.getLogger(__name__)
def start_server():
if openerp.multi_process:
_logger.info("FTP disabled in multiprocess mode")
return
HOST = config.get('ftp_server_host', '127.0.0.1')
PORT = int(config.get('ftp_server_port', '8021'))
PASSIVE_PORTS = None

View File

@ -45,8 +45,8 @@ class test_message_compose(TestMailBase):
# Mail data
_subject1 = 'Pigs'
_subject2 = 'Bird'
_body_html1 = 'Fans of Pigs, unite !\n<p>Admin</p>\n'
_body_html2 = 'I am angry !\n<p>Admin</p>\n'
_body_html1 = '<div><p>Fans of Pigs, unite !\n</p><p>Admin</p></div>'
_body_html2 = '<div><p>I am angry !\n</p><p>Admin</p></div>'
_attachments = [
{'name': 'First', 'datas_fname': 'first.txt', 'datas': base64.b64encode('My first attachment')},
{'name': 'Second', 'datas_fname': 'second.txt', 'datas': base64.b64encode('My second attachment')}
@ -147,7 +147,7 @@ class test_message_compose(TestMailBase):
message_pids = [partner.id for partner in compose.partner_ids]
partner_ids = [p_a_id]
self.assertEqual(compose.subject, '${object.name}', 'mail.compose.message subject incorrect')
self.assertEqual(compose.body, '${object.description}', 'mail.compose.message body incorrect')
self.assertEqual(compose.body, '<p>${object.description}</p>', 'mail.compose.message body incorrect')
self.assertEqual(set(message_pids), set(partner_ids), 'mail.compose.message partner_ids incorrect')
# 2. Post the comment, get created message

View File

@ -32,7 +32,7 @@
<field name="color">Black</field>
<field name="location">Grand-Rosiere</field>
<field name="doors">5</field>
<field name="driver_id" ref="base.user_demo" />
<field name="driver_id" ref="base.user_demo_res_partner" />
<field name="acquisition_date" eval="time.strftime('%Y-%m-%d 2:00:00')" />
<field name="state_id" ref="vehicle_state_active"/>
<field name="odometer_unit">kilometers</field>

View File

@ -94,9 +94,9 @@ class google_docs_ir_attachment(osv.osv):
client = self._auth(cr, uid)
# fetch and copy the original document
try:
original_resource = client.get_resource_by_id(gdoc_template_id)
doc = client.GetDoc(gdoc_template_id)
#copy the document you choose in the configuration
copy_resource = client.copy_resource(original_resource, name_gdocs)
copy_resource = client.copy(doc, name_gdocs)
except:
raise osv.except_osv(_('Google Docs Error!'), _("Your resource id is not correct. You can find the id in the google docs URL."))
# create an ir.attachment

View File

@ -16,7 +16,7 @@ var _t = instance.web._t,
var view = self.getParent();
var ids = ( view.fields_view.type != "form" )? view.groups.get_selection().ids : [ view.datarecord.id ];
if( !_.isEmpty(ids) ){
view.sidebar_context().done(function (context) {
view.sidebar_eval_context().done(function (context) {
var ds = new instance.web.DataSet(this, 'ir.attachment', context);
ds.call('google_doc_get', [view.dataset.model, ids, context]).done(function(r) {
if (r == 'False') {

View File

@ -215,7 +215,9 @@ class hr_employee(osv.osv):
try:
(model, mail_group_id) = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'group_all_employees')
employee = self.browse(cr, uid, employee_id, context=context)
self.pool.get('mail.group').message_post(cr, uid, [mail_group_id], body='Welcome to %s! Please help them take the first steps with OpenERP!' % (employee.name), context=context)
self.pool.get('mail.group').message_post(cr, uid, [mail_group_id],
body='Welcome to %s! Please help them take the first steps with OpenERP!' % (employee.name),
subtype='mail.mt_comment', context=context)
except:
pass # group deleted: do not push a message
return employee_id

View File

@ -140,7 +140,7 @@
<field name="arch" type="xml">
<xpath expr="//group[@name='active_group']" position="before">
<group string="Appraisals">
<field name="evaluation_plan_id" on_change="onchange_evaluation_plan_id(evaluation_plan_id, evaluation_date)"/>
<field name="evaluation_plan_id"/>
<field name="evaluation_date"/>
</group>
</xpath>

View File

@ -94,9 +94,8 @@ class lunch_order(osv.Model):
"""
today = datetime.now().isoweekday()
assert 1 <= today <= 7, "Should be between 1 and 7"
mapping = dict((idx, name) for idx, name in enumerate('monday tuestday wednesday thursday friday saturday sunday'.split()))
if today in mapping:
return mapping[today]
mapping = dict((idx, name) for idx, name in enumerate('days monday tuesday wednesday thursday friday saturday sunday'.split()))
return alert[mapping[today]]
def can_display_alert(self, alert):
"""

View File

@ -96,6 +96,16 @@ class mail_group(osv.Model):
'alias_domain': False, # always hide alias during creation
}
def _generate_header_description(self, cr, uid, group, context=None):
header = ''
if group.description:
header = '%s' % group.description
if group.alias_id and group.alias_id.alias_name and group.alias_id.alias_domain:
if header:
header = '%s<br/>' % header
return '%sGroup email gateway: %s@%s' % (header, group.alias_id.alias_name, group.alias_id.alias_domain)
return header
def _subscribe_users(self, cr, uid, ids, context=None):
for mail_group in self.browse(cr, uid, ids, context=context):
partner_ids = []
@ -126,6 +136,7 @@ class mail_group(osv.Model):
# Create group and alias
mail_group_id = super(mail_group, self).create(cr, uid, vals, context=context)
mail_alias.write(cr, uid, [vals['alias_id']], {"alias_force_thread_id": mail_group_id}, context)
group = self.browse(cr, uid, mail_group_id, context=context)
# Create client action for this group and link the menu to it
ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'action_mail_group_feeds')
@ -137,11 +148,11 @@ class mail_group(osv.Model):
'context': {'default_model': 'mail.group', 'default_res_id': mail_group_id, 'search_default_message_unread': True},
'res_model': 'mail.message',
'thread_level': 1,
'header_description': vals.get('description'),
'header_description': self._generate_header_description(cr, uid, group, context=context)
}
cobj = self.pool.get('ir.actions.client')
newref = cobj.copy(cr, SUPERUSER_ID, ref[1], default={'params': str(params), 'name': vals['name']}, context=context)
mobj.write(cr, SUPERUSER_ID, menu_id, { 'action': 'ir.actions.client,' + str(newref), 'mail_group_id': mail_group_id}, context=context)
mobj.write(cr, SUPERUSER_ID, menu_id, {'action': 'ir.actions.client,' + str(newref), 'mail_group_id': mail_group_id}, context=context)
if vals.get('group_ids'):
self._subscribe_users(cr, uid, [mail_group_id], context=context)
@ -164,18 +175,18 @@ class mail_group(osv.Model):
result = super(mail_group, self).write(cr, uid, ids, vals, context=context)
if vals.get('group_ids'):
self._subscribe_users(cr, uid, ids, context=context)
# if description is changed: update client action
if vals.get('description'):
# if description, name or alias is changed: update client action
if vals.get('description') or vals.get('name') or vals.get('alias_id') or vals.get('alias_name'):
cobj = self.pool.get('ir.actions.client')
for action in [group.menu_id.action for group in self.browse(cr, uid, ids, context=context)]:
new_params = action.params
new_params['header_description'] = vals.get('description')
new_params['header_description'] = self._generate_header_description(cr, uid, group, context=context)
cobj.write(cr, SUPERUSER_ID, [action.id], {'params': str(new_params)}, context=context)
# if name is changed: update menu
if vals.get('name'):
mobj = self.pool.get('ir.ui.menu')
mobj.write(cr, SUPERUSER_ID,
[group.menu_id.id for group in self.browse(cr, uid, ids, context=context)],
mobj.write(cr, SUPERUSER_ID,
[group.menu_id.id for group in self.browse(cr, uid, ids, context=context)],
{'name': vals.get('name')}, context=context)
return result

View File

@ -309,13 +309,15 @@ class mail_thread(osv.AbstractModel):
def message_track(self, cr, uid, ids, tracked_fields, initial_values, context=None):
def convert_for_display(value, field_obj):
def convert_for_display(value, col_info):
if not value and col_info['type'] == 'boolean':
return 'False'
if not value:
return ''
if field_obj['type'] == 'many2one':
if col_info['type'] == 'many2one':
return value[1]
if field_obj['type'] == 'selection':
return dict(field_obj['selection'])[value]
if col_info['type'] == 'selection':
return dict(col_info['selection'])[value]
return value
def format_message(message_description, tracked_values):
@ -489,7 +491,13 @@ class mail_thread(osv.AbstractModel):
for alias in mail_alias.browse(cr, uid, alias_ids, context=context):
user_id = alias.alias_user_id.id
if not user_id:
user_id = self._message_find_user_id(cr, uid, message, context=context)
# TDE note: this could cause crashes, because no clue that the user
# that send the email has the right to create or modify a new document
# Fallback on user_id = uid
# Note: recognized partners will be added as followers anyway
# user_id = self._message_find_user_id(cr, uid, message, context=context)
user_id = uid
_logger.debug('No matching user_id for the alias %s', alias.alias_name)
routes.append((alias.alias_model_id.model, alias.alias_force_thread_id, \
eval(alias.alias_defaults), user_id))
_logger.debug('Routing mail with Message-Id %s: direct alias match: %r', message_id, routes)
@ -959,7 +967,7 @@ class mail_thread(osv.AbstractModel):
# 3. Post message
return self.message_post(cr, uid, thread_id=thread_id, body=body,
type=msg_type, subtype=msg_subtype, parent_id=parent_id,
attachment_ids=attachment_ids, partner_ids=partner_ids, context=context, **kwargs)
attachment_ids=attachment_ids, partner_ids=list(partner_ids), context=context, **kwargs)
#------------------------------------------------------
# Followers API

View File

@ -51,9 +51,9 @@ class res_partner_mail(osv.Model):
"""
if isinstance(thread_id, (list, tuple)):
thread_id = thread_id[0]
if type == 'email':
if kwargs.get('type') == 'email':
partner_ids = kwargs.get('partner_ids', [])
if thread_id not in partner_ids:
if thread_id not in [command[1] for command in partner_ids]:
partner_ids.append((4, thread_id))
kwargs['partner_ids'] = partner_ids
thread_id = False

View File

@ -122,7 +122,7 @@ class res_users(osv.Model):
context['thread_model'] = 'res.partner'
if isinstance(thread_id, (list, tuple)):
thread_id = thread_id[0]
return self.browse(cr, uid, thread_id).partner_id.id
return self.browse(cr, SUPERUSER_ID, thread_id).partner_id.id
def message_post_user_api(self, cr, uid, thread_id, context=None, **kwargs):
""" Redirect the posting of message on res.users to the related partner.
@ -139,9 +139,16 @@ class res_users(osv.Model):
return self.pool.get('res.partner').message_post(cr, uid, partner_id, context=context, **kwargs)
def message_update(self, cr, uid, ids, msg_dict, update_vals=None, context=None):
partner_id = self.browse(cr, uid, ids)[0].partner_id.id
return self.pool.get('res.partner').message_update(cr, uid, [partner_id], msg_dict,
update_vals=update_vals, context=context)
for id in ids:
partner_id = self.browse(cr, SUPERUSER_ID, id).partner_id.id
self.pool.get('res.partner').message_update(cr, uid, [partner_id], msg_dict, update_vals=update_vals, context=context)
return True
def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None):
for id in ids:
partner_id = self.browse(cr, SUPERUSER_ID, id).partner_id.id
self.pool.get('res.partner').message_subscribe(cr, uid, [partner_id], partner_ids, subtype_ids=subtype_ids, context=context)
return True
class res_users_mail_group(osv.Model):

View File

@ -59,6 +59,10 @@
left:0; top: 0; bottom: 0; width: 40px;
overflow: hidden;
}
.openerp .oe_mail .oe_msg .oe_msg_left a,
.openerp .oe_mail .oe_msg .oe_msg_left img{
border: 0;
}
.openerp .oe_mail .oe_msg .oe_msg_icon{
width: 32px;
margin: 4px;
@ -184,7 +188,7 @@
transition: all 0.1s linear;
}
.openerp .oe_mail .oe_msg .oe_msg_icons .oe_star:hover a{
color: #FFF6C0;
color: #FFF670;
text-shadow: 0px 1px #FFA162,0px -1px #FFA162, -1px 0px #FFA162, 1px 0px #FFA162, 0px 3px 3px rgba(0,0,0,0.1);
}
.openerp .oe_mail .oe_msg .oe_msg_icons .oe_star.oe_starred a{
@ -220,6 +224,21 @@
height: 24px;
width: 100%;
}
.openerp .oe_mail .oe_msg.oe_msg_composer_compact .oe_compact{
height: 24px;
width: 100%;
padding: 2px 4px;
border: 1px solid #CCC;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
background: white;
font-size: 14px;
color: #AAA;
font-style: italic;
word-spacing: 3px;
cursor: text;
}
/* d) I.E. tweaks for Message action icons */
@ -585,6 +604,7 @@
.openerp .oe_followers .oe_partner {
height: 32px;
overflow: hidden;
white-space: nowrap;
}
.openerp .oe_followers .oe_partner img{
width: 32px;
@ -593,7 +613,9 @@
}
.openerp .oe_followers .oe_remove_follower{
cursor: pointer;
float: right;
position: absolute;
right: 0px;
line-height: 20px;
}
.openerp .oe_followers .oe_show_more{

View File

@ -471,7 +471,7 @@ openerp.mail = function (session) {
bind_events: function () {
var self = this;
this.$('textarea.oe_compact').on('focus', _.bind( this.on_compose_expandable, this));
this.$('.oe_compact').on('click', _.bind( this.on_compose_expandable, this));
// set the function called when attachments are added
this.$('input.oe_form_binary_file').on('change', _.bind( this.on_attachment_change, this) );
@ -479,18 +479,16 @@ openerp.mail = function (session) {
this.$('.oe_cancel').on('click', _.bind( this.on_cancel, this) );
this.$('.oe_post').on('click', _.bind( this.on_message_post, this) );
this.$('.oe_full').on('click', _.bind( this.on_compose_fullmail, this, this.id ? 'reply' : 'comment') );
/* stack for don't close the compose form if the user click on a button */
this.$('.oe_msg_left, .oe_msg_center').on('mousedown', _.bind( function () { this.stay_open = true; }, this));
this.$('.oe_msg_left, .oe_msg_content').on('mouseup', _.bind( function () { this.$('textarea').focus(); }, this));
var ev_stay = {};
ev_stay.mouseup = ev_stay.keydown = ev_stay.focus = function () { self.stay_open = false; };
this.$('textarea:not(.oe_compact)').on(ev_stay);
this.$('textarea:not(.oe_compact)').autosize();
this.$('textarea').on(ev_stay);
this.$('textarea').autosize();
// auto close
this.$('textarea:not(.oe_compact)').on('blur', _.bind( this.on_compose_expandable, this));
this.$('textarea').on('blur', _.bind( this.on_compose_expandable, this));
// event: delete child attachments off the oe_msg_attachment_list box
this.$(".oe_msg_attachment_list").on('click', '.oe_delete', this.on_attachment_delete);
@ -567,11 +565,14 @@ openerp.mail = function (session) {
check_recipient_partners: function (emails) {
var self = this;
var deferreds = [];
for (var i = 0; i < emails.length; i++) {
deferreds.push($.Deferred());
}
var ds_partner = new session.web.DataSetSearch(this, 'res.partner');
_.each(emails, function (email) {
ds_partner.call('search', [[['email', '=', email]]]).then(function (partner_ids) {
ds_partner.call('search', [[['email', 'ilike', email]]]).then(function (partner_ids) {
var deferred = deferreds[_.indexOf(emails, email)];
if (!partner_ids.length) {
var deferred = $.Deferred();
var pop = new session.web.form.FormOpenPopup(this);
pop.show_element(
'res.partner',
@ -588,11 +589,14 @@ openerp.mail = function (session) {
pop.on('write_completed, closed', self, function () {
deferred.resolve();
});
deferreds.push(deferred);
}
else {
deferred.resolve();
}
return deferred;
});
});
return $.when.apply( $, deferreds );
return $.when.apply( $, deferreds ).done();
},
on_message_post: function (event) {

View File

@ -218,7 +218,9 @@ openerp_mail_followers = function(session, mail) {
var subtype_list_ul = this.$('.oe_subtype_list');
subtype_list_ul.empty();
var records = data[this.view.datarecord.id || this.view.dataset.ids[0]].message_subtype_data;
if (records.length > 1) {
var nb_subtype = 0;
_(records).each(function (record) {nb_subtype++;});
if (nb_subtype > 1) {
_(records).each(function (record, record_name) {
record.name = record_name;
record.followed = record.followed || undefined;

View File

@ -39,9 +39,11 @@
</div>
</div>
<div t-if="widget.show_compact_message and !widget.show_composer and !widget.options.readonly" t-attf-class="oe_msg oe_msg_composer_compact #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}">
<textarea t-if="widget.options.compose_placeholder" class="field_text oe_compact" t-att-placeholder="widget.options.compose_placeholder"/>
<textarea t-if="!widget.options.compose_placeholder and !widget.options.view_mailbox" class="field_text oe_compact" placeholder="Write to the followers of this document..."/>
<textarea t-if="!widget.options.compose_placeholder and widget.options.view_mailbox" class="field_text oe_compact" placeholder="Share with my followers..."/>
<div class="field_text oe_compact">
<t t-if="widget.options.compose_placeholder" t-raw="widget.options.compose_placeholder"/>
<t t-if="!widget.options.compose_placeholder and !widget.options.view_mailbox">Write to the followers of this document...</t>
<t t-if="!widget.options.compose_placeholder and widget.options.view_mailbox">Share with my followers...</t>
</div>
</div>
<span t-if="!(widget.show_compact_message and !widget.show_composer) and !widget.show_composer" class="oe_placeholder_compose"></span>
</t>

View File

@ -143,7 +143,7 @@ class test_mail(TestMailBase):
mail_text = MAIL_TEMPLATE_PLAINTEXT.format(to='groups@example.com', subject='frogs', extra='', msg_id=test_msg_id)
self.mail_thread.message_process(cr, uid, None, mail_text)
new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0])
self.assertEqual(new_mail.body, '\n<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>\n',
self.assertEqual(new_mail.body, '<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>',
'plaintext mail incorrectly parsed')
# Do: post a new message, with a known partner
@ -340,11 +340,11 @@ class test_mail(TestMailBase):
# Mail data
_subject = 'Pigs'
_mail_subject = '%s posted on %s' % (user_raoul.name, group_pigs.name)
_body1 = 'Pigs rules'
_mail_body1 = 'Pigs rules\n<div><p>Raoul</p></div>\n'
_body1 = '<p>Pigs rules</p>'
_mail_body1 = '<p>Pigs rules</p>\n<div><p>Raoul</p></div>\n'
_mail_bodyalt1 = 'Pigs rules\nRaoul\n'
_body2 = '<html>Pigs rules</html>'
_mail_body2 = html_sanitize('<html>Pigs rules\n<div><p>Raoul</p></div>\n</html>')
_mail_body2 = '<div><p>Pigs rules</p></div>\n<div><p>Raoul</p></div>'
_mail_bodyalt2 = 'Pigs rules\nRaoul'
_attachments = [('First', 'My first attachment'), ('Second', 'My second attachment')]
@ -487,7 +487,7 @@ class test_mail(TestMailBase):
message = group_pigs.message_ids[0]
# Test: mail.message: subject, body inside pre
self.assertEqual(message.subject, _subject, 'mail.message incorrect subject')
self.assertEqual(message.body, _body, 'mail.message incorrect body')
self.assertEqual(message.body, '<p>%s</p>' % _body, 'mail.message incorrect body')
# Test: mail.message: notified_partner_ids = entries in mail.notification: group_pigs fans (a, b) + mail.compose.message partner_ids (c, d)
msg_pids = [partner.id for partner in message.notified_partner_ids]
test_pids = [p_b_id, p_c_id, p_d_id]
@ -541,9 +541,9 @@ class test_mail(TestMailBase):
self.assertIn(message2.id, test_msg_ids, 'Bird did not receive its mass mailing message')
# Test: mail.message: subject, body
self.assertEqual(message1.subject, _subject, 'mail.message subject incorrect')
self.assertEqual(message1.body, group_pigs.description, 'mail.message body incorrect')
self.assertEqual(message1.body, '<p>%s</p>' % group_pigs.description, 'mail.message body incorrect')
self.assertEqual(message2.subject, _subject, 'mail.message subject incorrect')
self.assertEqual(message2.body, group_bird.description, 'mail.message body incorrect')
self.assertEqual(message2.body, '<p>%s</p>' % group_bird.description, 'mail.message body incorrect')
def test_30_needaction(self):
""" Tests for mail.message needaction. """
@ -634,7 +634,7 @@ class test_mail(TestMailBase):
# Test: first produced message: no subtype, name change tracked
last_msg = self.group_pigs.message_ids[-1]
self.assertFalse(last_msg.subtype_id, 'tracked: message should not have been linked to a subtype')
self.assertIn('SelectedGroupOnly&#8594;Public', _strip_string_spaces(last_msg.body), 'tracked: message body incorrect')
self.assertIn(u'SelectedGroupOnly\u2192Public', _strip_string_spaces(last_msg.body), 'tracked: message body incorrect')
self.assertIn('Pigs', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field')
# Test: change name as supername, public as private -> 2 subtypes
@ -645,13 +645,13 @@ class test_mail(TestMailBase):
last_msg = self.group_pigs.message_ids[-2]
self.assertEqual(last_msg.subtype_id.id, mt_private_id, 'tracked: message should be linked to mt_private subtype')
self.assertIn('Private public', last_msg.body, 'tracked: message body does not hold the subtype description')
self.assertIn('Pigs&#8594;supername', _strip_string_spaces(last_msg.body), 'tracked: message body incorrect')
self.assertIn(u'Pigs\u2192supername', _strip_string_spaces(last_msg.body), 'tracked: message body incorrect')
# Test: second produced message: mt_name_supername
last_msg = self.group_pigs.message_ids[-3]
self.assertEqual(last_msg.subtype_id.id, mt_name_supername_id, 'tracked: message should be linked to mt_name_supername subtype')
self.assertIn('Supername name', last_msg.body, 'tracked: message body does not hold the subtype description')
self.assertIn('Public&#8594;Private', _strip_string_spaces(last_msg.body), 'tracked: message body incorrect')
self.assertIn('Pigs&#8594;supername', _strip_string_spaces(last_msg.body), 'tracked feature: message body does not hold always tracked field')
self.assertIn(u'Public\u2192Private', _strip_string_spaces(last_msg.body), 'tracked: message body incorrect')
self.assertIn(u'Pigs\u2192supername', _strip_string_spaces(last_msg.body), 'tracked feature: message body does not hold always tracked field')
# Test: change public as public, group_public_id -> 1 subtype, name always tracked
self.mail_group.write(cr, self.user_raoul_id, [self.group_pigs_id], {'public': 'public', 'group_public_id': group_system_id})
@ -661,8 +661,8 @@ class test_mail(TestMailBase):
last_msg = self.group_pigs.message_ids[-4]
self.assertEqual(last_msg.subtype_id.id, mt_group_public_id, 'tracked: message should not be linked to any subtype')
self.assertIn('Group changed', last_msg.body, 'tracked: message body does not hold the subtype description')
self.assertIn('Private&#8594;Public', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold changed tracked field')
self.assertIn('HumanResources/Employee&#8594;Administration/Settings', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field')
self.assertIn(u'Private\u2192Public', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold changed tracked field')
self.assertIn(u'HumanResources/Employee\u2192Administration/Settings', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field')
# Test: change not tracked field, no tracking message
self.mail_group.write(cr, self.user_raoul_id, [self.group_pigs_id], {'description': 'Dummy'})

View File

@ -19,6 +19,7 @@
#
##############################################################################
from openerp import tools
from openerp.osv import osv
from openerp.osv import fields
from openerp.tools.translate import _
@ -60,6 +61,12 @@ class invite_wizard(osv.osv_memory):
# send an email
if wizard.message:
# add signature
user_id = self.pool.get("res.users").read(cr, uid, [uid], fields=["signature"], context=context)[0]
signature = user_id and user_id["signature"] or ''
if signature:
wizard.message = tools.append_content_to_html(wizard.message, signature, plaintext=True, container_tag='div')
# send mail to new followers
for follower_id in new_follower_ids:
mail_mail = self.pool.get('mail.mail')
# the invite wizard should create a private message not related to any object -> no model, no res_id

View File

@ -1,12 +1,51 @@
openerp.portal_anonymous = function(instance) {
instance.web.Session.include({
load_translations: function() {
var self = this;
// browser_lang can contain 'xx' or 'xx_XX'
// we use the 'xx' to find matching languages installed in the DB
var browser_lang = (navigator.language || navigator.userLanguage).replace('-', '_');
// By default for anonymous session.user_context.lang === 'en_US',
// so do nothing if browser_lang is contained in 'en_US' (like 'en' or 'en_US')
if (this.username === 'anonymous' && this.user_context.lang.indexOf(browser_lang) === -1) {
return (new instance.web.Model('res.lang')).query(['code', 'iso_code'])
.filter([['code', 'like', browser_lang.substring(0, 2).toLowerCase()]]).all()
.then(function(langs) {
// If langs is empty (OpenERP doesn't support the language),
// then don't change session.user_context.lang
if (langs.length > 0) {
// Try to get the right user preference in the browser, else
// get the shortest language returned ('xx' country code) or
// just the first one
var l = _.filter(langs, function(lang) { return lang.code === browser_lang || lang.iso_code === browser_lang; });
if (!_.isEmpty(l)) {
self.user_context.lang = l[0].code;
} else {
l = _.filter(langs, function(lang) {
return lang.iso_code === _.pluck(langs, 'iso_code')
.sort(function(a, b) {
return a.length - b.length;
})[0];
});
self.user_context.lang = l[0].code;
}
}
return self.rpc('/web/webclient/translations', { mods: self.module_list, lang: self.user_context.lang }).done(function(trans) {
instance.web._t.database.set_bundle(trans);
});
});
}
return this._super();
},
});
instance.web.Login.include({
start: function() {
var self = this;
return $.when(this._super()).then(function() {
var params = $.deparam($.param.querystring());
var dblist = self.db_list || [];
if (!self.session.session_is_valid() && dblist.length === 1 && (!params.token || !params.login)) {
if (!self.session.session_is_valid() && dblist.length === 1 && _.isEmpty(self.params)) {
self.remember_credentials = false;
// XXX get login/pass from server (via a rpc call) ?
return self.do_login(dblist[0], 'anonymous', 'anonymous');
@ -51,14 +90,6 @@ openerp.portal_anonymous = function(instance) {
}
return false;
},
// Avoid browser preloading
show_application: function() {
var params = $.deparam($.param.querystring());
if (!!params.token || !!params.login) {
return this.show_login();
}
return this._super();
},
});
};

View File

@ -1089,6 +1089,14 @@ class task(base_stage, osv.osv):
return True
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
if not vals.get('stage_id') and vals.get('project_id'):
ctx = context.copy()
ctx['default_project_id'] = vals['project_id']
vals['stage_id'] = self._get_default_stage_id(cr, uid, context=ctx)
elif not vals.get('stage_id') and context.get('default_project_id'):
vals['stage_id'] = self._get_default_stage_id(cr, uid, context=context)
task_id = super(task, self).create(cr, uid, vals, context=context)
self._store_history(cr, uid, [task_id], context=context)
return task_id

View File

@ -451,7 +451,6 @@
<div><b><field name="name"/></b></div>
<div>
<field name="project_id"/><br/>
<div class="oe_ellipsis"><t t-raw="record.description.value"/></div>
<t t-if="record.date_deadline.raw_value and record.date_deadline.raw_value lt (new Date())" t-set="red">oe_kanban_text_red</t>
<span t-attf-class="#{red || ''}"><i><field name="date_deadline"/></i></span>
</div>

View File

@ -64,6 +64,17 @@ class project_issue(base_stage, osv.osv):
},
}
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
if not vals.get('stage_id') and vals.get('project_id'):
ctx = context.copy()
ctx['default_project_id'] = vals['project_id']
vals['stage_id'] = self._get_default_stage_id(cr, uid, context=ctx)
elif not vals.get('stage_id') and context.get('default_project_id'):
vals['stage_id'] = self._get_default_stage_id(cr, uid, context=context)
return super(project_issue, self).create(cr, uid, vals, context=context)
def _get_default_project_id(self, cr, uid, context=None):
""" Gives default project by checking if present in the context """
return self._resolve_project_id_from_context(cr, uid, context=context)
@ -514,7 +525,7 @@ class project_issue(base_stage, osv.osv):
}
for line in msg.get('body', '').split('\n'):
line = line.strip()
res = tools.misc.command_re.match(line)
res = tools.command_re.match(line)
if res and maps.get(res.group(1).lower(), False):
key = maps.get(res.group(1).lower())
update_vals[key] = res.group(2).lower()

View File

@ -178,6 +178,13 @@ class sale_order(osv.osv):
result[line.order_id.id] = True
return result.keys()
def _get_default_shop(self, cr, uid, context=None):
company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
shop_ids = self.pool.get('sale.shop').search(cr, uid, [('company_id','=',company_id)], context=context)
if not shop_ids:
raise osv.except_osv(_('Error!'), _('There is no default shop for the current user\'s company!'))
return shop_ids[0]
_columns = {
'name': fields.char('Order Reference', size=64, required=True,
readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, select=True),
@ -251,6 +258,7 @@ class sale_order(osv.osv):
'user_id': lambda obj, cr, uid, context: uid,
'name': lambda obj, cr, uid, context: '/',
'invoice_quantity': 'order',
'shop_id': _get_default_shop,
'partner_invoice_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['invoice'])['invoice'],
'partner_shipping_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['delivery'])['delivery'],
}

View File

@ -156,7 +156,7 @@
<field name="partner_id" on_change="onchange_partner_id(partner_id, context)" domain="[('customer','=',True)]" context="{'search_default_customer':1, 'show_address': 1}" options='{"always_reload": True}'/>
<field name="partner_invoice_id" groups="sale.group_delivery_invoice_address" context="{'default_type':'invoice'}"/>
<field name="partner_shipping_id" groups="sale.group_delivery_invoice_address" context="{'default_type':'delivery'}"/>
<field name="project_id" context="{'partner_id':partner_id, 'pricelist_id':pricelist_id, 'default_name':name}" groups="sale.group_analytic_accounting" domain="[('type','in',['view','normal','contract'])]"/>
<field name="project_id" context="{'partner_id':partner_id, 'pricelist_id':pricelist_id, 'default_name':name, 'default_type': 'contract'}" groups="sale.group_analytic_accounting" domain="[('type','in',['view','normal','contract'])]"/>
</group>
<group>
<field name="date_order"/>
@ -246,7 +246,7 @@
<div class="oe_subtotal_footer_separator oe_inline">
<label for="amount_total" />
<button name="button_dummy"
states="draft" string="(update)" type="object" class="oe_edit_only oe_link"/>
states="draft,sent" string="(update)" type="object" class="oe_edit_only oe_link"/>
</div>
<field name="amount_total" nolabel="1" class="oe_subtotal_footer_separator" widget='monetary' options="{'currency_field': 'currency_id'}"/>
</group>