[MERGE] Sync with upstream (saas-4)
bzr revid: tde@openerp.com-20140507090057-24hacfi1jxs6ob5g bzr revid: tde@openerp.com-20140509124711-v82rqm57nyc0jlcm
This commit is contained in:
commit
ad65e32cc2
|
@ -1026,7 +1026,7 @@ class account_move_line(osv.osv):
|
|||
if opening_reconciliation:
|
||||
obj_move_rec.write(cr, uid, unlink_ids, {'opening_reconciliation': False})
|
||||
obj_move_rec.unlink(cr, uid, unlink_ids)
|
||||
if all_moves:
|
||||
if len(all_moves) >= 2:
|
||||
obj_move_line.reconcile_partial(cr, uid, all_moves, 'auto',context=context)
|
||||
return True
|
||||
|
||||
|
|
|
@ -542,18 +542,17 @@ class account_analytic_account(osv.osv):
|
|||
'nodestroy': True,
|
||||
}
|
||||
|
||||
def on_change_template(self, cr, uid, ids, template_id, date_start=False, fix_price_invoices=False, invoice_on_timesheets=False, recurring_invoices=False, context=None):
|
||||
def on_change_template(self, cr, uid, ids, template_id, date_start=False, context=None):
|
||||
if not template_id:
|
||||
return {}
|
||||
obj_analytic_line = self.pool.get('account.analytic.invoice.line')
|
||||
res = super(account_analytic_account, self).on_change_template(cr, uid, ids, template_id, date_start=date_start, context=context)
|
||||
|
||||
template = self.browse(cr, uid, template_id, context=context)
|
||||
|
||||
if not fix_price_invoices:
|
||||
if not ids:
|
||||
res['value']['fix_price_invoices'] = template.fix_price_invoices
|
||||
res['value']['amount_max'] = template.amount_max
|
||||
if not invoice_on_timesheets:
|
||||
if not ids:
|
||||
res['value']['invoice_on_timesheets'] = template.invoice_on_timesheets
|
||||
res['value']['hours_qtt_est'] = template.hours_qtt_est
|
||||
|
||||
|
@ -561,7 +560,7 @@ class account_analytic_account(osv.osv):
|
|||
res['value']['to_invoice'] = template.to_invoice.id
|
||||
if template.pricelist_id.id:
|
||||
res['value']['pricelist_id'] = template.pricelist_id.id
|
||||
if not recurring_invoices:
|
||||
if not ids:
|
||||
invoice_line_ids = []
|
||||
for x in template.recurring_invoice_line_ids:
|
||||
invoice_line_ids.append((0, 0, {
|
||||
|
@ -690,8 +689,9 @@ class account_analytic_account(osv.osv):
|
|||
}
|
||||
return invoice
|
||||
|
||||
def _prepare_invoice_lines(self, cr, uid, contract, fiscal_position, context=None):
|
||||
def _prepare_invoice_lines(self, cr, uid, contract, fiscal_position_id, context=None):
|
||||
fpos_obj = self.pool.get('account.fiscal.position')
|
||||
fiscal_position = fpos_obj.browse(cr, uid, fiscal_position_id, context=context)
|
||||
invoice_lines = []
|
||||
for line in contract.recurring_invoice_line_ids:
|
||||
|
||||
|
|
|
@ -38,9 +38,6 @@
|
|||
<field name="partner_id" position="attributes">
|
||||
<attribute name="attrs">{'required': [('type','=','contract'),'|','|',('fix_price_invoices','=',True), ('invoice_on_timesheets', '=', True), ('recurring_invoices', '=', True)]}</attribute>
|
||||
</field>
|
||||
<field name="template_id" position="attributes">
|
||||
<attribute name="on_change">on_change_template(template_id, date_start, fix_price_invoices, invoice_on_timesheets, recurring_invoices)</attribute>
|
||||
</field>
|
||||
<xpath expr='//group[@name="invoice_on_timesheets"]' position="replace">
|
||||
</xpath>
|
||||
<xpath expr='//separator[@name="description"]' position='before'>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<field name="name" />
|
||||
<field name="client_id" />
|
||||
<field name="enabled" />
|
||||
<field name="body" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="auth_endpoint" />
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
<record model="crm.case.stage" id="stage_lead2">
|
||||
<field name="name">Dead</field>
|
||||
<field name="case_default">1</field>
|
||||
<field name="fold">1</field>
|
||||
<field name="probability">0</field>
|
||||
<field name="on_change">1</field>
|
||||
<field name="sequence">30</field>
|
||||
|
|
|
@ -88,7 +88,7 @@ Find duplicates, merge leads and assign them to the right salesperson in one ope
|
|||
<p class="oe_mt32">
|
||||
Get your opportunities organized to stay focused on the best deals. Manage all your customer interactions from the opportunity like emails, phone calls, internal notes, meetings and quotations.
|
||||
</p><p>
|
||||
Follow opportunities that interrests you to get notified upon specific events: deal won or lost, stage changed, new customer demand, etc.
|
||||
Follow opportunities that interest you to get notified upon specific events: deal won or lost, stage changed, new customer demand, etc.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<field name="subject">Your registration at ${object.event_id.name}</field>
|
||||
<field name="body_html"><![CDATA[
|
||||
<p>Hello ${object.name},</p>
|
||||
<p>The event ${object.event_id.name} that you registered for is confirmed and will be held from ${object.event_id.date_begin} to ${object.event_id.date_end}.
|
||||
<p>The event ${object.event_id.name} that you registered for is confirmed and will be held from ${object.event_id.date_begin_located.strftime('%Y-%m-%d %H:%M:%S (%Z)')} to ${object.event_id.date_end_located.strftime('%Y-%m-%d %H:%M:%S (%Z)')}.
|
||||
For any further information please contact our event department.</p>
|
||||
<p>Thank you for your participation!</p>
|
||||
<p>Best regards</p>]]></field>
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import pytz
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from datetime import datetime, timedelta
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
@ -150,6 +151,22 @@ class event_event(osv.osv):
|
|||
continue
|
||||
return res
|
||||
|
||||
def _compute_date_tz(self, cr, uid, ids, fld, arg, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
res = {}
|
||||
for event in self.browse(cr, uid, ids, context=context):
|
||||
ctx = dict(context, tz=(event.date_tz or 'UTC'))
|
||||
if fld == 'date_begin_located':
|
||||
date_to_convert = event.date_begin
|
||||
elif fld == 'date_end_located':
|
||||
date_to_convert = event.date_end
|
||||
res[event.id] = fields.datetime.context_timestamp(cr, uid, datetime.strptime(date_to_convert, DEFAULT_SERVER_DATETIME_FORMAT), context=ctx)
|
||||
return res
|
||||
|
||||
def _tz_get(self, cr, uid, context=None):
|
||||
return [(x, x) for x in pytz.all_timezones]
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Event Name', size=64, required=True, translate=True, readonly=False, states={'done': [('readonly', True)]}),
|
||||
'user_id': fields.many2one('res.users', 'Responsible User', readonly=False, states={'done': [('readonly', True)]}),
|
||||
|
@ -169,8 +186,11 @@ class event_event(osv.osv):
|
|||
store={'event.registration': (_get_events_from_registrations, ['state'], 10),
|
||||
'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['seats_max', 'registration_ids'], 20)}),
|
||||
'registration_ids': fields.one2many('event.registration', 'event_id', 'Registrations', readonly=False, states={'done': [('readonly', True)]}),
|
||||
'date_tz': fields.selection(_tz_get, string='Timezone'),
|
||||
'date_begin': fields.datetime('Start Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'date_end': fields.datetime('End Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'date_begin_located': fields.function(_compute_date_tz, string='Start Date Located', type="datetime"),
|
||||
'date_end_located': fields.function(_compute_date_tz, string='End Date Located', type="datetime"),
|
||||
'state': fields.selection([
|
||||
('draft', 'Unconfirmed'),
|
||||
('cancel', 'Cancelled'),
|
||||
|
@ -197,7 +217,8 @@ class event_event(osv.osv):
|
|||
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'event.event', context=c),
|
||||
'user_id': lambda obj, cr, uid, context: uid,
|
||||
'organizer_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, context=c).company_id.partner_id.id,
|
||||
'address_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, context=c).company_id.partner_id.id
|
||||
'address_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, context=c).company_id.partner_id.id,
|
||||
'date_tz': lambda self, cr, uid, ctx: ctx.get('tz', "UTC"),
|
||||
}
|
||||
|
||||
def _check_seats_limit(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -83,6 +83,7 @@
|
|||
<field name="type" on_change="onchange_event_type(type,context)" />
|
||||
<field name="date_begin" on_change="onchange_start_date(date_begin,date_end)"/>
|
||||
<field name="date_end"/>
|
||||
<field name="date_tz" />
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
|
|
|
@ -217,15 +217,7 @@ class gamification_challenge(osv.Model):
|
|||
vals['user_ids'] = []
|
||||
vals['user_ids'] += [(4, user_id) for user_id in user_ids]
|
||||
|
||||
create_res = super(gamification_challenge, self).create(cr, uid, vals, context=context)
|
||||
|
||||
# subscribe new users to the challenge
|
||||
if vals.get('user_ids'):
|
||||
# done with browse after super to be sure catch all after orm process
|
||||
challenge = self.browse(cr, uid, create_res, context=context)
|
||||
self.message_subscribe_users(cr, uid, [challenge.id], [user.id for user in challenge.user_ids], context=context)
|
||||
|
||||
return create_res
|
||||
return super(gamification_challenge, self).create(cr, uid, vals, context=context)
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
if isinstance(ids, (int,long)):
|
||||
|
@ -240,6 +232,11 @@ class gamification_challenge(osv.Model):
|
|||
|
||||
write_res = super(gamification_challenge, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
if vals.get('report_message_frequency', 'never') != 'never':
|
||||
# _recompute_challenge_users do not set users for challenges with no reports, subscribing them now
|
||||
for challenge in self.browse(cr, uid, ids, context=context):
|
||||
self.message_subscribe(cr, uid, [challenge.id], [user.partner_id.id for user in challenge.user_ids], context=context)
|
||||
|
||||
if vals.get('state') == 'inprogress':
|
||||
self._recompute_challenge_users(cr, uid, ids, context=context)
|
||||
self._generate_goals_from_challenge(cr, uid, ids, context=context)
|
||||
|
@ -264,6 +261,9 @@ class gamification_challenge(osv.Model):
|
|||
- Create the missing goals (eg: modified the challenge to add lines)
|
||||
- Update every running challenge
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
# start scheduled challenges
|
||||
planned_challenge_ids = self.search(cr, uid, [
|
||||
('state', '=', 'draft'),
|
||||
|
@ -281,6 +281,9 @@ class gamification_challenge(osv.Model):
|
|||
if not ids:
|
||||
ids = self.search(cr, uid, [('state', '=', 'inprogress')], context=context)
|
||||
|
||||
# in cron mode, will do intermediate commits
|
||||
# TODO in trunk: replace by parameter
|
||||
context.update({'commit_gamification': True})
|
||||
return self._update_all(cr, uid, ids, context=context)
|
||||
|
||||
def _update_all(self, cr, uid, ids, context=None):
|
||||
|
@ -355,11 +358,6 @@ class gamification_challenge(osv.Model):
|
|||
if write_op:
|
||||
self.write(cr, uid, [challenge.id], {'user_ids': write_op}, context=context)
|
||||
|
||||
if to_remove_ids:
|
||||
self.message_unsubscribe_users(cr, uid, [challenge.id], to_remove_ids, context=None)
|
||||
if to_add_ids:
|
||||
self.message_subscribe_users(cr, uid, [challenge.id], to_add_ids, context=context)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
@ -393,9 +391,9 @@ class gamification_challenge(osv.Model):
|
|||
:param list(int) ids: the list of challenge concerned"""
|
||||
|
||||
goal_obj = self.pool.get('gamification.goal')
|
||||
to_update = []
|
||||
for challenge in self.browse(cr, uid, ids, context=context):
|
||||
(start_date, end_date) = start_end_date_for_period(challenge.period)
|
||||
to_update = []
|
||||
|
||||
# if no periodicity, use challenge dates
|
||||
if not start_date and challenge.start_date:
|
||||
|
@ -426,7 +424,15 @@ class gamification_challenge(osv.Model):
|
|||
|
||||
cr.execute(query, query_params)
|
||||
user_with_goal_ids = cr.dictfetchall()
|
||||
user_without_goal_ids = list(set([user.id for user in challenge.user_ids]) - set([user['user_id'] for user in user_with_goal_ids]))
|
||||
|
||||
participant_user_ids = [user.id for user in challenge.user_ids]
|
||||
user_without_goal_ids = list(set(participant_user_ids) - set([user['user_id'] for user in user_with_goal_ids]))
|
||||
user_squating_challenge_ids = list(set([user['user_id'] for user in user_with_goal_ids]) - set(participant_user_ids))
|
||||
if user_squating_challenge_ids:
|
||||
# users that used to match the challenge
|
||||
goal_to_remove_ids = goal_obj.search(cr, uid, [('challenge_id', '=', challenge.id), ('user_id', 'in', user_squating_challenge_ids)], context=context)
|
||||
goal_obj.unlink(cr, uid, goal_to_remove_ids, context=context)
|
||||
|
||||
|
||||
values = {
|
||||
'definition_id': line.definition_id.id,
|
||||
|
@ -614,9 +620,10 @@ class gamification_challenge(osv.Model):
|
|||
ctx.update({'challenge_lines': lines_boards})
|
||||
body_html = temp_obj.render_template(cr, uid, challenge.report_template_id.body_html, 'gamification.challenge', challenge.id, context=ctx)
|
||||
|
||||
# send to every follower of the challenge
|
||||
# send to every follower and participant of the challenge
|
||||
self.message_post(cr, uid, challenge.id,
|
||||
body=body_html,
|
||||
partner_ids=[user.partner_id.id for user in challenge.user_ids],
|
||||
context=context,
|
||||
subtype='mail.mt_comment')
|
||||
if challenge.report_message_group_id:
|
||||
|
@ -698,34 +705,35 @@ class gamification_challenge(osv.Model):
|
|||
rewarded_users = []
|
||||
challenge_ended = end_date == yesterday.strftime(DF) or force
|
||||
if challenge.reward_id and challenge_ended or challenge.reward_realtime:
|
||||
for user in challenge.user_ids:
|
||||
reached_goal_ids = self.pool.get('gamification.goal').search(cr, uid, [
|
||||
# not using start_date as intemportal goals have a start date but no end_date
|
||||
reached_goals = self.pool.get('gamification.goal').read_group(cr, uid, [
|
||||
('challenge_id', '=', challenge.id),
|
||||
('user_id', '=', user.id),
|
||||
('start_date', '=', start_date),
|
||||
('end_date', '=', end_date),
|
||||
('state', '=', 'reached')
|
||||
], context=context)
|
||||
if len(reached_goal_ids) == len(challenge.line_ids):
|
||||
], fields=['user_id'], groupby=['user_id'], context=context)
|
||||
for reach_goals_user in reached_goals:
|
||||
if reach_goals_user['user_id_count'] == len(challenge.line_ids):
|
||||
# the user has succeeded every assigned goal
|
||||
user_id = reach_goals_user['user_id'][0]
|
||||
if challenge.reward_realtime:
|
||||
badges = self.pool['gamification.badge.user'].search(cr, uid, [
|
||||
('challenge_id', '=', challenge.id),
|
||||
('badge_id', '=', challenge.reward_id.id),
|
||||
('user_id', '=', user.id),
|
||||
('user_id', '=', user_id),
|
||||
], count=True, context=context)
|
||||
if badges > 0:
|
||||
# has already recieved the badge for this challenge
|
||||
continue
|
||||
self.reward_user(cr, uid, user.id, challenge.reward_id.id, challenge.id, context=context)
|
||||
rewarded_users.append(user)
|
||||
self.reward_user(cr, uid, user_id, challenge.reward_id.id, challenge.id, context=context)
|
||||
rewarded_users.append(user_id)
|
||||
|
||||
if challenge_ended:
|
||||
# open chatter message
|
||||
message_body = _("The challenge %s is finished." % challenge.name)
|
||||
|
||||
if rewarded_users:
|
||||
message_body += _("<br/>Reward (badge %s) for every succeeding user was sent to %s." % (challenge.reward_id.name, ", ".join([user.name for user in rewarded_users])))
|
||||
user_names = self.pool['res.users'].name_get(cr, uid, rewarded_users, context=context)
|
||||
message_body += _("<br/>Reward (badge %s) for every succeeding user was sent to %s." % (challenge.reward_id.name, ", ".join([name for (user_id, name) in user_names])))
|
||||
else:
|
||||
message_body += _("<br/>Nobody has succeeded to reach every goal, no badge is rewared for this challenge.")
|
||||
|
||||
|
@ -746,7 +754,10 @@ class gamification_challenge(osv.Model):
|
|||
self.reward_user(cr, uid, third_user.id, challenge.reward_second_id.id, challenge.id, context=context)
|
||||
message_body += "<br/> 3. %s - %s" % (third_user.name, challenge.reward_third_id.name)
|
||||
|
||||
self.message_post(cr, uid, challenge.id, body=message_body, context=context)
|
||||
self.message_post(cr, uid, challenge.id,
|
||||
partner_ids=[user.partner_id.id for user in challenge.user_ids],
|
||||
body=message_body,
|
||||
context=context)
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
@ -138,7 +138,6 @@ class gamification_goal(osv.Model):
|
|||
|
||||
_name = 'gamification.goal'
|
||||
_description = 'Gamification goal instance'
|
||||
_inherit = 'mail.thread'
|
||||
|
||||
def _get_completion(self, cr, uid, ids, field_name, arg, context=None):
|
||||
"""Return the percentage of completeness of the goal, between 0 and 100"""
|
||||
|
@ -226,8 +225,7 @@ class gamification_goal(osv.Model):
|
|||
temp_obj = self.pool.get('email.template')
|
||||
template_id = self.pool['ir.model.data'].get_object(cr, uid, 'gamification', 'email_template_goal_reminder', context)
|
||||
body_html = temp_obj.render_template(cr, uid, template_id.body_html, 'gamification.goal', goal.id, context=context)
|
||||
|
||||
self.message_post(cr, uid, goal.id, body=body_html, partner_ids=[goal.user_id.partner_id.id], context=context, subtype='mail.mt_comment')
|
||||
self.pool['mail.thread'].message_post(cr, uid, 0, body=body_html, partner_ids=[goal.user_id.partner_id.id], context=context, subtype='mail.mt_comment')
|
||||
return {'to_update': True}
|
||||
return {}
|
||||
|
||||
|
@ -241,9 +239,9 @@ class gamification_goal(osv.Model):
|
|||
the target value being reached, the goal is set as failed."""
|
||||
if context is None:
|
||||
context = {}
|
||||
commit = context.get('commit_gamification', False)
|
||||
|
||||
goals_by_definition = {}
|
||||
goals_to_write = {}
|
||||
all_goals = {}
|
||||
for goal in self.browse(cr, uid, ids, context=context):
|
||||
if goal.state in ('draft', 'canceled'):
|
||||
|
@ -251,10 +249,10 @@ class gamification_goal(osv.Model):
|
|||
continue
|
||||
|
||||
goals_by_definition.setdefault(goal.definition_id, []).append(goal)
|
||||
goals_to_write[goal.id] = {}
|
||||
all_goals[goal.id] = goal
|
||||
|
||||
for definition, goals in goals_by_definition.items():
|
||||
goals_to_write = dict((goal.id, {}) for goal in goals)
|
||||
if definition.computation_mode == 'manually':
|
||||
for goal in goals:
|
||||
goals_to_write[goal.id].update(self._check_remind_delay(cr, uid, goal, context))
|
||||
|
@ -361,6 +359,8 @@ class gamification_goal(osv.Model):
|
|||
value['closed'] = True
|
||||
if value:
|
||||
self.write(cr, uid, [goal.id], value, context=context)
|
||||
if commit:
|
||||
cr.commit()
|
||||
return True
|
||||
|
||||
def action_start(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -86,10 +86,6 @@
|
|||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
@ -249,6 +249,12 @@ class hr_employee(osv.osv):
|
|||
'color': 0,
|
||||
}
|
||||
|
||||
def copy_data(self, cr, uid, ids, default=None, context=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
default.update({'child_ids': False})
|
||||
return super(hr_employee, self).copy_data(cr, uid, ids, default, context=context)
|
||||
|
||||
def _broadcast_welcome(self, cr, uid, employee_id, context=None):
|
||||
""" Broadcast the welcome message to all users in the employee company. """
|
||||
employee = self.browse(cr, uid, employee_id, context=context)
|
||||
|
@ -421,18 +427,23 @@ class hr_department(osv.osv):
|
|||
res.append((record['id'], name))
|
||||
return res
|
||||
|
||||
def copy(self, cr, uid, ids, default=None, context=None):
|
||||
def copy_data(self, cr, uid, ids, default=None, context=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
default = default.copy()
|
||||
default['member_ids'] = []
|
||||
return super(hr_department, self).copy(cr, uid, ids, default, context=context)
|
||||
return super(hr_department, self).copy_data(cr, uid, ids, default, context=context)
|
||||
|
||||
|
||||
class res_users(osv.osv):
|
||||
_name = 'res.users'
|
||||
_inherit = 'res.users'
|
||||
|
||||
def copy_data(self, cr, uid, ids, default=None, context=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
default.update({'employee_ids': False})
|
||||
return super(res_users, self).copy_data(cr, uid, ids, default, context=context)
|
||||
|
||||
_columns = {
|
||||
'employee_ids': fields.one2many('hr.employee', 'user_id', 'Related employees'),
|
||||
}
|
||||
|
|
|
@ -22,9 +22,11 @@
|
|||
import time
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from pytz import timezone
|
||||
import pytz
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class hr_timesheet_sheet(osv.osv):
|
||||
|
@ -397,22 +399,56 @@ class hr_attendance(osv.osv):
|
|||
attendance_ids.extend([row[0] for row in cr.fetchall()])
|
||||
return attendance_ids
|
||||
|
||||
def _get_current_sheet(self, cr, uid, employee_id, date=False, context=None):
|
||||
def _get_attendance_employee_tz(self, cr, uid, employee_id, date, context=None):
|
||||
""" Simulate timesheet in employee timezone
|
||||
|
||||
Return the attendance date in string format in the employee
|
||||
tz converted from utc timezone as we consider date of employee
|
||||
timesheet is in employee timezone
|
||||
"""
|
||||
employee_obj = self.pool['hr.employee']
|
||||
|
||||
tz = False
|
||||
if employee_id:
|
||||
employee = employee_obj.browse(cr, uid, employee_id, context=context)
|
||||
tz = employee.user_id.partner_id.tz
|
||||
|
||||
if not date:
|
||||
date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
# ending date with no time to avoid timesheet with early date_to
|
||||
date_to = date[0:10]+' 00:00:00'
|
||||
# limit=1 because only one sheet possible for an employee between 2 dates
|
||||
sheet_ids = self.pool.get('hr_timesheet_sheet.sheet').search(cr, uid, [
|
||||
('date_to', '>=', date_to), ('date_from', '<=', date),
|
||||
('employee_id', '=', employee_id)
|
||||
], limit=1, context=context)
|
||||
|
||||
att_tz = timezone(tz or 'utc')
|
||||
|
||||
attendance_dt = datetime.strptime(date, DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
att_tz_dt = pytz.utc.localize(attendance_dt)
|
||||
att_tz_dt = att_tz_dt.astimezone(att_tz)
|
||||
# We take only the date omiting the hours as we compare with timesheet
|
||||
# date_from which is a date format thus using hours would lead to
|
||||
# be out of scope of timesheet
|
||||
att_tz_date_str = datetime.strftime(att_tz_dt, DEFAULT_SERVER_DATE_FORMAT)
|
||||
return att_tz_date_str
|
||||
|
||||
def _get_current_sheet(self, cr, uid, employee_id, date=False, context=None):
|
||||
|
||||
sheet_obj = self.pool['hr_timesheet_sheet.sheet']
|
||||
if not date:
|
||||
date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
|
||||
att_tz_date_str = self._get_attendance_employee_tz(
|
||||
cr, uid, employee_id,
|
||||
date=date, context=context)
|
||||
sheet_ids = sheet_obj.search(cr, uid,
|
||||
[('date_from', '<=', att_tz_date_str),
|
||||
('date_to', '>=', att_tz_date_str),
|
||||
('employee_id', '=', employee_id)],
|
||||
limit=1, context=context)
|
||||
return sheet_ids and sheet_ids[0] or False
|
||||
|
||||
def _sheet(self, cursor, user, ids, name, args, context=None):
|
||||
res = {}.fromkeys(ids, False)
|
||||
for attendance in self.browse(cursor, user, ids, context=context):
|
||||
res[attendance.id] = self._get_current_sheet(cursor, user, attendance.employee_id.id, attendance.name, context=context)
|
||||
res[attendance.id] = self._get_current_sheet(
|
||||
cursor, user, attendance.employee_id.id, attendance.name,
|
||||
context=context)
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
|
@ -434,10 +470,13 @@ class hr_attendance(osv.osv):
|
|||
|
||||
sheet_id = context.get('sheet_id') or self._get_current_sheet(cr, uid, vals.get('employee_id'), vals.get('name'), context=context)
|
||||
if sheet_id:
|
||||
att_tz_date_str = self._get_attendance_employee_tz(
|
||||
cr, uid, vals.get('employee_id'),
|
||||
date=vals.get('name'), context=context)
|
||||
ts = self.pool.get('hr_timesheet_sheet.sheet').browse(cr, uid, sheet_id, context=context)
|
||||
if ts.state not in ('draft', 'new'):
|
||||
raise osv.except_osv(_('Error!'), _('You can not enter an attendance in a submitted timesheet. Ask your manager to reset it before adding attendance.'))
|
||||
elif ts.date_from > vals.get('name') or ts.date_to < vals.get('name'):
|
||||
elif ts.date_from > att_tz_date_str or ts.date_to < att_tz_date_str:
|
||||
raise osv.except_osv(_('User Error!'), _('You can not enter an attendance date outside the current timesheet dates.'))
|
||||
return super(hr_attendance,self).create(cr, uid, vals, context=context)
|
||||
|
||||
|
@ -589,4 +628,3 @@ class res_company(osv.osv):
|
|||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ from openerp import SUPERUSER_ID
|
|||
from openerp import http
|
||||
from openerp.http import request
|
||||
from openerp.addons.web.controllers.main import content_disposition
|
||||
import mimetypes
|
||||
|
||||
|
||||
class MailController(http.Controller):
|
||||
|
@ -19,10 +20,11 @@ class MailController(http.Controller):
|
|||
if res:
|
||||
filecontent = base64.b64decode(res.get('base64'))
|
||||
filename = res.get('filename')
|
||||
content_type = mimetypes.guess_type(filename)
|
||||
if filecontent and filename:
|
||||
return request.make_response(
|
||||
filecontent,
|
||||
headers=[('Content-Type', 'application/octet-stream'),
|
||||
headers=[('Content-Type', content_type[0] or 'application/octet-stream'),
|
||||
('Content-Disposition', content_disposition(filename))])
|
||||
return request.not_found()
|
||||
|
||||
|
|
|
@ -234,13 +234,17 @@ class mail_mail(osv.Model):
|
|||
:return: True
|
||||
"""
|
||||
ir_mail_server = self.pool.get('ir.mail_server')
|
||||
ir_attachment = self.pool['ir.attachment']
|
||||
|
||||
for mail in self.browse(cr, SUPERUSER_ID, ids, context=context):
|
||||
try:
|
||||
# handle attachments
|
||||
attachments = []
|
||||
for attach in mail.attachment_ids:
|
||||
attachments.append((attach.datas_fname, base64.b64decode(attach.datas)))
|
||||
# load attachment binary data with a separate read(), as prefetching all
|
||||
# `datas` (binary field) could bloat the browse cache, triggerring
|
||||
# soft/hard mem limits with temporary data.
|
||||
attachment_ids = [a.id for a in mail.attachment_ids]
|
||||
attachments = [(a['datas_fname'], base64.b64decode(a['datas']))
|
||||
for a in ir_attachment.read(cr, SUPERUSER_ID, attachment_ids,
|
||||
['datas_fname', 'datas'])]
|
||||
# specific behavior to customize the send email for notified partners
|
||||
email_list = []
|
||||
if mail.email_to:
|
||||
|
@ -289,6 +293,14 @@ class mail_mail(osv.Model):
|
|||
# /!\ can't use mail.state here, as mail.refresh() will cause an error
|
||||
# see revid:odo@openerp.com-20120622152536-42b2s28lvdv3odyr in 6.1
|
||||
self._postprocess_sent_message(cr, uid, mail, context=context, mail_sent=mail_sent)
|
||||
_logger.info('Mail with ID %r and Message-Id %r successfully sent', mail.id, mail.message_id)
|
||||
except MemoryError:
|
||||
# prevent catching transient MemoryErrors, bubble up to notify user or abort cron job
|
||||
# instead of marking the mail as failed
|
||||
_logger.exception('MemoryError while processing mail with ID %r and Msg-Id %r. '\
|
||||
'Consider raising the --limit-memory-hard startup option',
|
||||
mail.id, mail.message_id)
|
||||
raise
|
||||
except Exception as e:
|
||||
_logger.exception('failed sending mail.mail %s', mail.id)
|
||||
mail.write({'state': 'exception'})
|
||||
|
|
|
@ -434,14 +434,14 @@ class mail_thread(osv.AbstractModel):
|
|||
fol_obj.unlink(cr, SUPERUSER_ID, fol_ids, context=context)
|
||||
return res
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
def copy_data(self, cr, uid, id, default=None, context=None):
|
||||
# avoid tracking multiple temporary changes during copy
|
||||
context = dict(context or {}, mail_notrack=True)
|
||||
|
||||
default = default or {}
|
||||
default['message_ids'] = []
|
||||
default['message_follower_ids'] = []
|
||||
return super(mail_thread, self).copy(cr, uid, id, default=default, context=context)
|
||||
return super(mail_thread, self).copy_data(cr, uid, id, default=default, context=context)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Automatically log tracked fields
|
||||
|
|
|
@ -34,14 +34,14 @@ class project_configuration(osv.TransientModel):
|
|||
}
|
||||
|
||||
def get_default_alias_domain(self, cr, uid, ids, context=None):
|
||||
alias_domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", context=context)
|
||||
if not alias_domain:
|
||||
alias_domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", default=None, context=context)
|
||||
if alias_domain is None:
|
||||
domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "web.base.url", context=context)
|
||||
try:
|
||||
alias_domain = urlparse.urlsplit(domain).netloc.split(':')[0]
|
||||
except Exception:
|
||||
pass
|
||||
return {'alias_domain': alias_domain}
|
||||
return {'alias_domain': alias_domain or False}
|
||||
|
||||
def set_alias_domain(self, cr, uid, ids, context=None):
|
||||
config_parameters = self.pool.get("ir.config_parameter")
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
<record id="res_company_oerp_us" model="res.company">
|
||||
<field name="partner_id" ref="res_partner_oerp_us"/>
|
||||
<field name="parent_id" ref="res_company_oerp_editor"/>
|
||||
<field name="currency_id" ref="base.EUR"/>
|
||||
<field name="currency_id" ref="base.USD"/>
|
||||
<field name="name">OpenERP US</field>
|
||||
</record>
|
||||
<record id="res_company_oerp_be" model="res.company">
|
||||
|
@ -400,7 +400,7 @@
|
|||
</record>
|
||||
<record id="project.project_project_3" model="project.project">
|
||||
<field name="company_id" ref="res_company_oerp_us"/>
|
||||
<field name="currency_id" ref="base.EUR"/>
|
||||
<field name="currency_id" ref="base.USD"/>
|
||||
</record>
|
||||
<record id="project.project_project_4" model="project.project">
|
||||
<field name="company_id" ref="res_company_oerp_be"/>
|
||||
|
|
|
@ -1167,8 +1167,8 @@ class pos_order(osv.osv):
|
|||
return self.write(cr, uid, ids, {'state': 'payment'}, context=context)
|
||||
|
||||
def action_paid(self, cr, uid, ids, context=None):
|
||||
self.create_picking(cr, uid, ids, context=context)
|
||||
self.write(cr, uid, ids, {'state': 'paid'}, context=context)
|
||||
self.create_picking(cr, uid, ids, context=context)
|
||||
return True
|
||||
|
||||
def action_cancel(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -105,7 +105,9 @@ class product_product(osv.osv):
|
|||
'compute_child': False
|
||||
})
|
||||
|
||||
qty = product.qty_available
|
||||
# qty_available depends of the location in the context
|
||||
qty = self.read(cr, uid, [product.id], ['qty_available'], context=c)[0]['qty_available']
|
||||
|
||||
diff = product.standard_price - new_price
|
||||
if not diff: raise osv.except_osv(_('Error!'), _("No difference between standard price and new price!"))
|
||||
if qty:
|
||||
|
|
|
@ -2307,7 +2307,9 @@ class stock_move(osv.osv):
|
|||
|
||||
# if product is set to average price and a specific value was entered in the picking wizard,
|
||||
# we use it
|
||||
if move.product_id.cost_method == 'average' and move.price_unit:
|
||||
if move.location_dest_id.usage != 'internal' and move.product_id.cost_method == 'average':
|
||||
reference_amount = qty * move.product_id.standard_price
|
||||
elif move.product_id.cost_method == 'average' and move.price_unit:
|
||||
reference_amount = qty * move.price_unit
|
||||
reference_currency_id = move.price_currency_id.id or reference_currency_id
|
||||
|
||||
|
|
|
@ -792,7 +792,7 @@
|
|||
</group>
|
||||
<notebook>
|
||||
<page string="Products">
|
||||
<field name="move_lines" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'picking_type': 'internal'}" options='{"reload_on_button": true}'/>
|
||||
<field name="move_lines" string="Stock Move" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'picking_type': 'internal'}" options='{"reload_on_button": true}'/>
|
||||
<field name="note" placeholder="Add an internal note..." class="oe_inline"/>
|
||||
</page>
|
||||
<page string="Additional Info">
|
||||
|
@ -926,7 +926,7 @@
|
|||
<field name="partner_id" on_change="onchange_partner_in(partner_id)" string="Customer" domain="[('customer','=',True)]" />
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='move_lines']" position="replace">
|
||||
<field name="move_lines" context="{'address_out_id': partner_id, 'picking_type': 'out', 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree'}" options='{"reload_on_button": true}'/>
|
||||
<field name="move_lines" string="Stock Move" context="{'address_out_id': partner_id, 'picking_type': 'out', 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree'}" options='{"reload_on_button": true}'/>
|
||||
</xpath>
|
||||
<xpath expr="/form/sheet" position="after">
|
||||
<div class="oe_chatter">
|
||||
|
@ -1053,7 +1053,7 @@
|
|||
<field name="partner_id" on_change="onchange_partner_in(partner_id)" string="Supplier" domain="[('supplier','=',True)]" context="{'default_supplier':1,'default_customer':0}"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='move_lines']" position="replace">
|
||||
<field name="move_lines" context="{'address_in_id': partner_id, 'picking_type': 'in', 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree'}" options='{"reload_on_button": true}'/>
|
||||
<field name="move_lines" string="Stock Move" context="{'address_in_id': partner_id, 'picking_type': 'in', 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree'}" options='{"reload_on_button": true}'/>
|
||||
</xpath>
|
||||
<xpath expr="/form/sheet" position="after">
|
||||
<div class="oe_chatter">
|
||||
|
@ -1340,7 +1340,7 @@
|
|||
|
||||
<group string="Locations" groups="stock.group_locations">
|
||||
<field name="location_id" domain="[('usage','<>','view')]"/>
|
||||
<field name="location_dest_id" domain="[('usage','=','internal')]" groups="stock.group_locations"/>
|
||||
<field name="location_dest_id" domain="[('usage','in', ['internal', 'supplier', 'customer'])]" groups="stock.group_locations"/>
|
||||
</group>
|
||||
|
||||
<group groups="stock.group_tracking_lot" string="Traceability">
|
||||
|
|
|
@ -62,14 +62,14 @@ class sale_order(osv.osv):
|
|||
message = False
|
||||
partner = self.pool.get('res.partner').browse(cr, uid, part, context=context)
|
||||
if partner.sale_warn != 'no-message':
|
||||
if partner.sale_warn == 'block':
|
||||
raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.sale_warn_msg)
|
||||
title = _("Warning for %s") % partner.name
|
||||
message = partner.sale_warn_msg
|
||||
warning = {
|
||||
'title': title,
|
||||
'message': message,
|
||||
}
|
||||
if partner.sale_warn == 'block':
|
||||
return {'value': {'partner_id': False}, 'warning': warning}
|
||||
|
||||
result = super(sale_order, self).onchange_partner_id(cr, uid, ids, part, context=context)
|
||||
|
||||
|
@ -90,14 +90,15 @@ class purchase_order(osv.osv):
|
|||
message = False
|
||||
partner = self.pool.get('res.partner').browse(cr, uid, part)
|
||||
if partner.purchase_warn != 'no-message':
|
||||
if partner.purchase_warn == 'block':
|
||||
raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.purchase_warn_msg)
|
||||
title = _("Warning for %s") % partner.name
|
||||
message = partner.purchase_warn_msg
|
||||
warning = {
|
||||
'title': title,
|
||||
'message': message
|
||||
}
|
||||
if partner.purchase_warn == 'block':
|
||||
return {'value': {'partner_id': False}, 'warning': warning}
|
||||
|
||||
result = super(purchase_order, self).onchange_partner_id(cr, uid, ids, part)
|
||||
|
||||
if result.get('warning',False):
|
||||
|
@ -123,15 +124,16 @@ class account_invoice(osv.osv):
|
|||
message = False
|
||||
partner = self.pool.get('res.partner').browse(cr, uid, partner_id)
|
||||
if partner.invoice_warn != 'no-message':
|
||||
if partner.invoice_warn == 'block':
|
||||
raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.invoice_warn_msg)
|
||||
|
||||
title = _("Warning for %s") % partner.name
|
||||
message = partner.invoice_warn_msg
|
||||
warning = {
|
||||
'title': title,
|
||||
'message': message
|
||||
}
|
||||
|
||||
if partner.invoice_warn == 'block':
|
||||
return {'value': {'partner_id': False}, 'warning': warning}
|
||||
|
||||
result = super(account_invoice, self).onchange_partner_id(cr, uid, ids, type, partner_id,
|
||||
date_invoice=date_invoice, payment_term=payment_term,
|
||||
partner_bank_id=partner_bank_id, company_id=company_id)
|
||||
|
@ -154,14 +156,15 @@ class stock_picking(osv.osv):
|
|||
title = False
|
||||
message = False
|
||||
if partner.picking_warn != 'no-message':
|
||||
if partner.picking_warn == 'block':
|
||||
raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.picking_warn_msg)
|
||||
title = _("Warning for %s") % partner.name
|
||||
message = partner.picking_warn_msg
|
||||
warning = {
|
||||
'title': title,
|
||||
'message': message
|
||||
}
|
||||
if partner.picking_warn == 'block':
|
||||
return {'value': {'partner_id': False}, 'warning': warning}
|
||||
|
||||
result = super(stock_picking, self).onchange_partner_in(cr, uid, ids, partner_id, context)
|
||||
if result.get('warning',False):
|
||||
warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
|
||||
|
@ -183,14 +186,15 @@ class stock_picking_in(osv.osv):
|
|||
title = False
|
||||
message = False
|
||||
if partner.picking_warn != 'no-message':
|
||||
if partner.picking_warn == 'block':
|
||||
raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.picking_warn_msg)
|
||||
title = _("Warning for %s") % partner.name
|
||||
message = partner.picking_warn_msg
|
||||
warning = {
|
||||
'title': title,
|
||||
'message': message
|
||||
}
|
||||
if partner.picking_warn == 'block':
|
||||
return {'value': {'partner_id': False}, 'warning': warning}
|
||||
|
||||
result = super(stock_picking_in, self).onchange_partner_in(cr, uid, ids, partner_id, context)
|
||||
if result.get('warning',False):
|
||||
warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
|
||||
|
@ -209,14 +213,15 @@ class stock_picking_out(osv.osv):
|
|||
title = False
|
||||
message = False
|
||||
if partner.picking_warn != 'no-message':
|
||||
if partner.picking_warn == 'block':
|
||||
raise osv.except_osv(_('Alert for %s!') % (partner.name), partner.picking_warn_msg)
|
||||
title = _("Warning for %s") % partner.name
|
||||
message = partner.picking_warn_msg
|
||||
warning = {
|
||||
'title': title,
|
||||
'message': message
|
||||
}
|
||||
if partner.picking_warn == 'block':
|
||||
return {'value': {'partner_id': False}, 'warning': warning}
|
||||
|
||||
result = super(stock_picking_out, self).onchange_partner_in(cr, uid, ids, partner_id, context)
|
||||
if result.get('warning',False):
|
||||
warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
|
||||
|
@ -256,12 +261,12 @@ class sale_order_line(osv.osv):
|
|||
message = False
|
||||
|
||||
if product_info.sale_line_warn != 'no-message':
|
||||
if product_info.sale_line_warn == 'block':
|
||||
raise osv.except_osv(_('Alert for %s!') % (product_info.name), product_info.sale_line_warn_msg)
|
||||
title = _("Warning for %s") % product_info.name
|
||||
message = product_info.sale_line_warn_msg
|
||||
warning['title'] = title
|
||||
warning['message'] = message
|
||||
if product_info.sale_line_warn == 'block':
|
||||
return {'value': {'product_id': False}, 'warning': warning}
|
||||
|
||||
result = super(sale_order_line, self).product_id_change( cr, uid, ids, pricelist, product, qty,
|
||||
uom, qty_uos, uos, name, partner_id,
|
||||
|
@ -288,12 +293,12 @@ class purchase_order_line(osv.osv):
|
|||
message = False
|
||||
|
||||
if product_info.purchase_line_warn != 'no-message':
|
||||
if product_info.purchase_line_warn == 'block':
|
||||
raise osv.except_osv(_('Alert for %s!') % (product_info.name), product_info.purchase_line_warn_msg)
|
||||
title = _("Warning for %s") % product_info.name
|
||||
message = product_info.purchase_line_warn_msg
|
||||
warning['title'] = title
|
||||
warning['message'] = message
|
||||
if product_info.purchase_line_warn == 'block':
|
||||
return {'value': {'product_id': False}, 'warning': warning}
|
||||
|
||||
result = super(purchase_order_line, self).onchange_product_id(cr, uid, ids, pricelist, product, qty, uom,
|
||||
partner_id, date_order, fiscal_position_id)
|
||||
|
|
|
@ -2,6 +2,7 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
|||
access_website_public,website,website.model_website,,1,0,0,0
|
||||
access_website,website,website.model_website,base.group_website_designer,1,1,1,1
|
||||
access_website_menu,access_website_menu,model_website_menu,,1,0,0,0
|
||||
access_website_menu_designer,Web Menu Manager,model_website_menu,base.group_website_designer,1,1,1,1
|
||||
access_website,web menu manager,website.model_website,base.group_website_designer,1,1,1,1
|
||||
access_website_converter_test,access_website_converter_test,model_website_converter_test,,1,1,1,1
|
||||
access_website_converter_test_sub,access_website_converter_test_sub,model_website_converter_test_sub,,1,1,1,1
|
||||
|
|
|
|
@ -124,6 +124,7 @@ class WebsiteForum(http.Controller):
|
|||
values.update({
|
||||
'main_object': tag or forum,
|
||||
'question_ids': question_ids,
|
||||
'question_count': question_count,
|
||||
'pager': pager,
|
||||
'tag': tag,
|
||||
'filters': filters,
|
||||
|
@ -134,7 +135,7 @@ class WebsiteForum(http.Controller):
|
|||
|
||||
@http.route(['/forum/<model("forum.forum"):forum>/faq'], type='http', auth="public", website=True, multilang=True)
|
||||
def forum_faq(self, forum, **post):
|
||||
values = self._prepare_forum_values(forum=forum, searches=dict(), **post)
|
||||
values = self._prepare_forum_values(forum=forum, searches=dict(), header={'is_guidelines': True}, **post)
|
||||
return request.website.render("website_forum.faq", values)
|
||||
|
||||
@http.route('/forum/get_tags', type='http', auth="public", multilang=True, methods=['GET'], website=True)
|
||||
|
@ -147,7 +148,7 @@ class WebsiteForum(http.Controller):
|
|||
def tags(self, forum, page=1, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
Tag = request.registry['forum.tag']
|
||||
obj_ids = Tag.search(cr, uid, [('forum_id', '=', forum.id)], limit=None, context=context)
|
||||
obj_ids = Tag.search(cr, uid, [('forum_id', '=', forum.id), ('posts_count', '>', 0)], limit=None, order='posts_count DESC', context=context)
|
||||
tags = Tag.browse(cr, uid, obj_ids, context=context)
|
||||
values = self._prepare_forum_values(forum=forum, searches={'tags': True}, **post)
|
||||
values.update({
|
||||
|
@ -426,7 +427,7 @@ class WebsiteForum(http.Controller):
|
|||
tag_count = User.search(cr, SUPERUSER_ID, [('karma', '>', 1)], count=True, context=context)
|
||||
pager = request.website.pager(url="/forum/users", total=tag_count, page=page, step=step, scope=30)
|
||||
|
||||
obj_ids = User.search(cr, SUPERUSER_ID, [('karma', '>', 1)], limit=step, offset=pager['offset'], context=context)
|
||||
obj_ids = User.search(cr, SUPERUSER_ID, [('karma', '>', 1)], limit=step, offset=pager['offset'], order='karma DESC', context=context)
|
||||
users = User.browse(cr, SUPERUSER_ID, obj_ids, context=context)
|
||||
searches['users'] = 'True'
|
||||
|
||||
|
@ -590,12 +591,13 @@ class WebsiteForum(http.Controller):
|
|||
|
||||
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment/<model("mail.message"):comment>/convert_to_answer', type='http', auth="public", multilang=True, website=True)
|
||||
def convert_comment_to_answer(self, forum, post, comment, **kwarg):
|
||||
values = {
|
||||
'content': comment.body,
|
||||
}
|
||||
body = comment.body
|
||||
request.registry['mail.message'].unlink(request.cr, request.uid, [comment.id], context=request.context)
|
||||
question = post.parent_id if post.parent_id else post
|
||||
return self.post_new(forum, question, **values)
|
||||
for answer in question.child_ids:
|
||||
if answer.create_uid.id == request.uid:
|
||||
return self.post_comment(forum, answer, comment=html2plaintext(body))
|
||||
return self.post_new(forum, question, content=body)
|
||||
|
||||
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/convert_to_comment', type='http', auth="user", multilang=True, website=True)
|
||||
def convert_answer_to_comment(self, forum, post, **kwarg):
|
||||
|
|
|
@ -13,10 +13,7 @@
|
|||
<field name="sequence" type="int">35</field>
|
||||
</record>
|
||||
|
||||
<record id="default_allow_auth_signup" model="ir.config_parameter">
|
||||
<field name="key">auth_signup.allow_uninvited</field>
|
||||
<field name="value" eval="True"/>
|
||||
</record>
|
||||
<function model="ir.config_parameter" name="set_param" eval="('auth_signup.allow_uninvited', True)" />
|
||||
|
||||
<!-- JUMP TO FORUM AT INSTALL -->
|
||||
<record id="action_open_forum" model="ir.actions.act_url">
|
||||
|
|
|
@ -70,6 +70,7 @@ class Post(osv.Model):
|
|||
_name = 'forum.post'
|
||||
_description = 'Forum Post'
|
||||
_inherit = ['mail.thread', 'website.seo.metadata']
|
||||
_order = "is_correct DESC, vote_count DESC"
|
||||
|
||||
def _get_user_vote(self, cr, uid, ids, field_name, arg, context):
|
||||
res = dict.fromkeys(ids, 0)
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
background-color: #cccccc;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.question .badge-active {
|
||||
background-color: #428bca;
|
||||
}
|
||||
|
||||
.oe_grey {
|
||||
background-color: #eeeeee;
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
.badge
|
||||
background-color: #ccc
|
||||
margin-left: 4px
|
||||
.badge-active
|
||||
background-color: #428bca
|
||||
|
||||
.oe_grey
|
||||
background-color: #eeeeee
|
||||
|
|
|
@ -92,7 +92,8 @@
|
|||
</div>
|
||||
<div class="panel-body">
|
||||
<t t-raw="forum.description"/><br/>
|
||||
<a t-attf-href="/forum/#{slug(forum)}/faq" class="fa fa-arrow-right"> Read Guidelines</a>
|
||||
<a t-if="not header.get('is_guidelines')" t-attf-href="/forum/#{slug(forum)}/faq" class="fa fa-arrow-right"> Read Guidelines</a>
|
||||
<a t-if="header.get('is_guidelines')" t-attf-href="/forum/#{slug(forum)}" class="fa fa-arrow-right"> Back to <span t-field="forum.name"/></a>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="header.get('question_data')">
|
||||
|
@ -144,8 +145,11 @@
|
|||
<span t-if="not question.active"><b> [Deleted]</b></span>
|
||||
<span t-if="question.state == 'close'"><b> [Closed]</b></span>
|
||||
</div>
|
||||
<t t-foreach="question.tag_ids" t-as="tag">
|
||||
<a t-attf-href="/forum/#{ slug(forum) }/tag/#{ tag.id }/questions" class="badge pull-right" t-field="tag.name"/>
|
||||
<t t-foreach="question.tag_ids" t-as="question_tag">
|
||||
<a t-attf-href="/forum/#{ slug(forum) }/tag/#{slug(question_tag)}/questions">
|
||||
<span t-attf-class="pull-right badge #{tag and tag.name == question_tag.name and 'badge-active' ''}" t-field="question_tag.name"
|
||||
style="margin-right: 4px;"/>
|
||||
</a>
|
||||
</t>
|
||||
<div class="text-muted">
|
||||
by <a t-attf-href="/forum/#{ slug(forum) }/user/#{ question.create_uid.id }"
|
||||
|
@ -208,14 +212,14 @@
|
|||
<template id="forum_index" name="Forum">
|
||||
<t t-call="website_forum.header">
|
||||
<h1 class="page-header mt0">
|
||||
<t t-esc="len(question_ids)"/> <span>Questions</span>
|
||||
<t t-esc="question_count"/> <span>Questions</span>
|
||||
<t t-esc="search"/>
|
||||
<small class="dropdown" t-if="filters in ('all', 'unanswered','followed', 'tag')">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<t t-if="filters == 'all'">All</t>
|
||||
<t t-if="filters == 'unanswered'">Unanswered</t>
|
||||
<t t-if="filters == 'followed'">Followed</t>
|
||||
<t t-if="filters == 'tag'">Tag</t>
|
||||
<t t-if="tag"><span t-field="tag.name"/></t>
|
||||
<t t-if="sorting == 'date'"> by activity date</t>
|
||||
<t t-if="sorting == 'answered'"> by most answered</t>
|
||||
<t t-if="sorting == 'vote'"> by most voted</t>
|
||||
|
@ -332,12 +336,12 @@
|
|||
users having a high karma can see closed questions to moderate
|
||||
them.
|
||||
</p>
|
||||
<form t-attf-action="/forum/#{ slug(forum) }/question/#{slug(post)}/close" method="post" role="form" class="form-horizontal mt32 mb64">
|
||||
<form t-attf-action="/forum/#{ slug(forum) }/question/#{slug(question)}/close" method="post" role="form" class="form-horizontal mt32 mb64">
|
||||
<input name="post_id" t-att-value="question.id" type="hidden"/>
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label" for="reason">Question:</label>
|
||||
<div class="col-md-8 mt8">
|
||||
<span t-field="post.name"/>
|
||||
<span t-field="question.name"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -354,7 +358,7 @@
|
|||
<div class="col-md-offset-3 col-md-8">
|
||||
<button class="btn btn-primary">Close question</button>
|
||||
<span class="text-muted">or</span>
|
||||
<a class="btn btn-link" t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(post) }">back to question</a>
|
||||
<a class="btn btn-link" t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }">back to question</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -451,7 +455,7 @@
|
|||
<a class="text-muted fa fa-times" t-attf-href="/forum/#{ slug(forum) }/question/#{slug(question)}/ask_for_close"> Close</a>
|
||||
</li>
|
||||
<li t-if="question.state == 'close' and ((user.id == question.create_uid.id and can_close_own) or can_close_all)">
|
||||
<a class="text-muted fa fa-undo" t-attf-href="/forum/#{ slug(forum) }/question/#{slug(question)/reopen">reopen</a>
|
||||
<a class="text-muted fa fa-undo" t-attf-href="/forum/#{ slug(forum) }/question/#{slug(question)}/reopen"> Reopen</a>
|
||||
</li>
|
||||
<li t-if="(user.id == question.create_uid.id and can_edit_own) or can_edit_all">
|
||||
<a class="text-muted fa fa-edit" t-attf-href="/forum/#{ slug(forum) }/post/#{slug(question)}/edit"> Edit</a>
|
||||
|
@ -467,7 +471,7 @@
|
|||
<div>
|
||||
<span t-field="question.create_uid.image" t-field-options='{"widget": "image", "class":"pull-left img img-circle img-avatar"}'/>
|
||||
<div>
|
||||
<a t-attf-href="/forum/#{ slug(forum) }/user/#{ user.id }"
|
||||
<a t-attf-href="/forum/#{ slug(forum) }/user/#{ question.create_uid.id }"
|
||||
t-field="question.create_uid"
|
||||
t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
|
||||
style="display: inline-block;"/>
|
||||
|
|
|
@ -26,10 +26,10 @@
|
|||
}
|
||||
</style>
|
||||
<script>var partner_url = '<t t-raw="partner_url or ''"/>';</script>
|
||||
<script src="http://maps.google.com/maps/api/js?sensor=false"></script>
|
||||
<script src="//maps.google.com/maps/api/js?sensor=false"></script>
|
||||
<script type="text/javascript" t-attf-src="/google_map/partners.json?partner_ids=#{ partner_ids }"></script>
|
||||
<script type="text/javascript" src="/website_google_map/static/src/js/markerclusterer_compiled.js"></script>
|
||||
<script src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
|
||||
<script src="//code.jquery.com/jquery-1.6.1.min.js"></script>
|
||||
<script type="text/javascript" src="/website_google_map/static/src/js/google_map.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
<section class="oe_container oe_dark">
|
||||
<div class="oe_row">
|
||||
<h2 class="oe_slogan">Fine Tune Your Catalog</h2>
|
||||
<h3 class="oe_slogan">Boost Sales by Emphasazing Best Products</h3>
|
||||
<h3 class="oe_slogan">Boost Sales by Emphasizing Best Products</h3>
|
||||
<div class="oe_span6">
|
||||
<p class='oe_mt32'>
|
||||
Get a full control on how you display your products in
|
||||
|
|
Loading…
Reference in New Issue