[MERGE] Forward port of 7.0 until rev. 8534.
bzr revid: vmt@openerp.com-20130104093953-p92uylaux9u24isn
This commit is contained in:
commit
5e8f418d41
|
@ -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>
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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):
|
||||
'''
|
||||
|
|
|
@ -123,24 +123,10 @@
|
|||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_account_manual_reconcile_receivable" model="ir.actions.act_window">
|
||||
<field name="name">Reconcile Invoices & 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 & Payments"
|
||||
action="action_account_manual_reconcile_receivable"
|
||||
action="account.action_account_manual_reconcile"
|
||||
parent="menu_finance_followup"
|
||||
sequence="0"
|
||||
id="menu_manual_reconcile_followup"/>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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 <b>changed</b></field>
|
||||
<field name="description">Status changed</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
|
|
@ -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 <b>pending</b></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 <b>closed</b></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 <b>opened</b></field>
|
||||
<field name="description">Contract opened</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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])]"/>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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') {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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→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→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→Private', _strip_string_spaces(last_msg.body), 'tracked: message body incorrect')
|
||||
self.assertIn('Pigs→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→Public', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold changed tracked field')
|
||||
self.assertIn('HumanResources/Employee→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'})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
},
|
||||
});
|
||||
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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'],
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue