[MERGE] forward port of branch 8.0 up to c7648a1
This commit is contained in:
+5
-2
@@ -5,7 +5,10 @@ Contributing to Odoo
|
||||
|
||||
TL;DR
|
||||
|
||||
* Use this [template](https://github.com/odoo/odoo/wiki/Contributing#reporting-issues) when reporting issues, and please search for duplicates first!
|
||||
* The issues not linked to pull requests will be handled with a low priority.
|
||||
* If you [make a pull request](https://github.com/odoo/odoo/wiki/Contributing#making-pull-requests),
|
||||
do not create an issue! Use the PR description for that
|
||||
* Issues are handled with a much lower priority than pull requests
|
||||
* Use this [template](https://github.com/odoo/odoo/wiki/Contributing#reporting-issues)
|
||||
when reporting issues. Please search for duplicates first!
|
||||
* Pull requests must be made against the [correct version](https://github.com/odoo/odoo/wiki/Contributing#against-which-version-should-i-submit-a-patch)
|
||||
* There are restrictions on the kind of [changes allowed in stable series](https://github.com/odoo/odoo/wiki/Contributing#what-does-stable-mean)
|
||||
|
||||
@@ -1155,9 +1155,10 @@ class account_move_line(osv.osv):
|
||||
move_ids = set()
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
move_ids.add(line.move_id.id)
|
||||
context['journal_id'] = line.journal_id.id
|
||||
context['period_id'] = line.period_id.id
|
||||
result = super(account_move_line, self).unlink(cr, uid, [line.id], context=context)
|
||||
localcontext = dict(context)
|
||||
localcontext['journal_id'] = line.journal_id.id
|
||||
localcontext['period_id'] = line.period_id.id
|
||||
result = super(account_move_line, self).unlink(cr, uid, [line.id], context=localcontext)
|
||||
move_ids = list(move_ids)
|
||||
if check and move_ids:
|
||||
move_obj.validate(cr, uid, move_ids, context=context)
|
||||
|
||||
+56
-47
@@ -125,9 +125,9 @@ class calendar_attendee(osv.Model):
|
||||
def ics_datetime(idate, allday=False):
|
||||
if idate:
|
||||
if allday:
|
||||
return datetime.strptime(idate.split(' ')[0], DEFAULT_SERVER_DATE_FORMAT).replace(tzinfo=pytz.timezone('UTC'))
|
||||
return openerp.fields.Date.from_string(idate)
|
||||
else:
|
||||
return datetime.strptime(idate.split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT).replace(tzinfo=pytz.timezone('UTC'))
|
||||
return openerp.fields.Datetime.from_string(idate).replace(tzinfo=pytz.timezone('UTC'))
|
||||
return False
|
||||
|
||||
try:
|
||||
@@ -374,12 +374,12 @@ class calendar_alarm_manager(osv.AbstractModel):
|
||||
tuple_params += (partner_id, )
|
||||
|
||||
#Add filter on hours
|
||||
tuple_params += (seconds, seconds,)
|
||||
tuple_params += (seconds,)
|
||||
|
||||
cr.execute("""SELECT *
|
||||
FROM ( %s WHERE cal.active = True ) AS ALL_EVENTS
|
||||
WHERE ALL_EVENTS.first_alarm < (now() at time zone 'utc' + interval '%%s' second )
|
||||
AND ALL_EVENTS.last_alarm > (now() at time zone 'utc' - interval '%%s' second )
|
||||
AND ALL_EVENTS.last_event_date > (now() at time zone 'utc')
|
||||
""" % base_request, tuple_params)
|
||||
|
||||
for event_id, first_alarm, last_alarm, first_meeting, last_meeting, min_duration, max_duration, rule in cr.fetchall():
|
||||
@@ -396,20 +396,30 @@ class calendar_alarm_manager(osv.AbstractModel):
|
||||
|
||||
return res
|
||||
|
||||
def do_check_alarm_for_one_date(self, cr, uid, one_date, event, event_maxdelta, in_the_next_X_seconds, after=False, notif=True, mail=True, context=None):
|
||||
res = []
|
||||
alarm_type = []
|
||||
def do_check_alarm_for_one_date(self, cr, uid, one_date, event, event_maxdelta, in_the_next_X_seconds, after=False, notif=True, mail=True, missing=False, context=None):
|
||||
# one_date: date of the event to check (not the same that in the event browse if recurrent)
|
||||
# event: Event browse record
|
||||
# event_maxdelta: biggest duration from alarms for this event
|
||||
# in_the_next_X_seconds: looking in the future (in seconds)
|
||||
# after: if not False: will return alert if after this date (date as string - todo: change in master)
|
||||
# missing: if not False: will return alert even if we are too late
|
||||
# notif: Looking for type notification
|
||||
# mail: looking for type email
|
||||
|
||||
res = []
|
||||
|
||||
# TODO: replace notif and email in master by alarm_type + remove event_maxdelta and if using it
|
||||
alarm_type = []
|
||||
if notif:
|
||||
alarm_type.append('notification')
|
||||
if mail:
|
||||
alarm_type.append('email')
|
||||
|
||||
if one_date - timedelta(minutes=event_maxdelta) < datetime.now() + timedelta(seconds=in_the_next_X_seconds): # if an alarm is possible for this date
|
||||
if one_date - timedelta(minutes=(missing and 0 or event_maxdelta)) < datetime.now() + timedelta(seconds=in_the_next_X_seconds): # if an alarm is possible for this date
|
||||
for alarm in event.alarm_ids:
|
||||
if alarm.type in alarm_type and \
|
||||
one_date - timedelta(minutes=alarm.duration_minutes) < datetime.now() + timedelta(seconds=in_the_next_X_seconds) and \
|
||||
(not after or one_date - timedelta(minutes=alarm.duration_minutes) > datetime.strptime(after.split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT)):
|
||||
one_date - timedelta(minutes=(missing and 0 or alarm.duration_minutes)) < datetime.now() + timedelta(seconds=in_the_next_X_seconds) and \
|
||||
(not after or one_date - timedelta(minutes=alarm.duration_minutes) > openerp.fields.Datetime.from_string(after)):
|
||||
alert = {
|
||||
'alarm_id': alarm.id,
|
||||
'event_id': event.id,
|
||||
@@ -419,55 +429,53 @@ class calendar_alarm_manager(osv.AbstractModel):
|
||||
return res
|
||||
|
||||
def get_next_mail(self, cr, uid, context=None):
|
||||
now = openerp.fields.Datetime.to_string(datetime.now())
|
||||
|
||||
icp = self.pool['ir.config_parameter']
|
||||
last_notif_mail = icp.get_param(cr, SUPERUSER_ID, 'calendar.last_notif_mail', default=False) or now
|
||||
|
||||
try:
|
||||
cron = self.pool['ir.model.data'].get_object(
|
||||
cr, uid, 'calendar', 'ir_cron_scheduler_alarm', context=context)
|
||||
cron = self.pool['ir.model.data'].get_object(cr, uid, 'calendar', 'ir_cron_scheduler_alarm', context=context)
|
||||
except ValueError:
|
||||
_logger.error("Cron for " + self._name + " can not be identified !")
|
||||
return False
|
||||
|
||||
if cron.interval_type == "weeks":
|
||||
cron_interval = cron.interval_number * 7 * 24 * 60 * 60
|
||||
elif cron.interval_type == "days":
|
||||
cron_interval = cron.interval_number * 24 * 60 * 60
|
||||
elif cron.interval_type == "hours":
|
||||
cron_interval = cron.interval_number * 60 * 60
|
||||
elif cron.interval_type == "minutes":
|
||||
cron_interval = cron.interval_number * 60
|
||||
elif cron.interval_type == "seconds":
|
||||
cron_interval = cron.interval_number
|
||||
else:
|
||||
cron_interval = False
|
||||
interval_to_second = {
|
||||
"weeks": 7 * 24 * 60 * 60,
|
||||
"days": 24 * 60 * 60,
|
||||
"hours": 60 * 60,
|
||||
"minutes": 60,
|
||||
"seconds": 1
|
||||
}
|
||||
|
||||
if not cron_interval:
|
||||
if cron.interval_type not in interval_to_second.keys():
|
||||
_logger.error("Cron delay can not be computed !")
|
||||
return False
|
||||
|
||||
cron_interval = cron.interval_number * interval_to_second[cron.interval_type]
|
||||
|
||||
all_events = self.get_next_potential_limit_alarm(cr, uid, cron_interval, notif=False, context=context)
|
||||
|
||||
for event in all_events: # .values()
|
||||
max_delta = all_events[event]['max_duration']
|
||||
curEvent = self.pool.get('calendar.event').browse(cr, uid, event, context=context)
|
||||
for curEvent in self.pool.get('calendar.event').browse(cr, uid, all_events.keys(), context=context):
|
||||
max_delta = all_events[curEvent.id]['max_duration']
|
||||
|
||||
if curEvent.recurrency:
|
||||
bFound = False
|
||||
LastFound = False
|
||||
at_least_one = False
|
||||
last_found = False
|
||||
for one_date in self.pool.get('calendar.event').get_recurrent_date_by_event(cr, uid, curEvent, context=context):
|
||||
in_date_format = one_date.replace(tzinfo=None)
|
||||
LastFound = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, cron_interval, notif=False, context=context)
|
||||
if LastFound:
|
||||
for alert in LastFound:
|
||||
self.do_mail_reminder(cr, uid, alert, context=context)
|
||||
|
||||
if not bFound: # if it's the first alarm for this recurrent event
|
||||
bFound = True
|
||||
if bFound and not LastFound: # if the precedent event had an alarm but not this one, we can stop the search for this event
|
||||
last_found = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, 0, after=last_notif_mail, notif=False, missing=True, context=context)
|
||||
for alert in last_found:
|
||||
self.do_mail_reminder(cr, uid, alert, context=context)
|
||||
at_least_one = True # if it's the first alarm for this recurrent event
|
||||
if at_least_one and not last_found: # if the precedent event had an alarm but not this one, we can stop the search for this event
|
||||
break
|
||||
else:
|
||||
in_date_format = datetime.strptime(curEvent.start, DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
LastFound = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, cron_interval, notif=False, context=context)
|
||||
if LastFound:
|
||||
for alert in LastFound:
|
||||
self.do_mail_reminder(cr, uid, alert, context=context)
|
||||
last_found = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, 0, after=last_notif_mail, notif=False, missing=True, context=context)
|
||||
for alert in last_found:
|
||||
self.do_mail_reminder(cr, uid, alert, context=context)
|
||||
icp.set_param(cr, SUPERUSER_ID, 'calendar.last_notif_mail', now)
|
||||
|
||||
def get_next_notif(self, cr, uid, context=None):
|
||||
ajax_check_every_seconds = 300
|
||||
@@ -497,7 +505,7 @@ class calendar_alarm_manager(osv.AbstractModel):
|
||||
break
|
||||
else:
|
||||
in_date_format = datetime.strptime(curEvent.start, DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
LastFound = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, ajax_check_every_seconds, partner.calendar_last_notif_ack, mail=False, context=context)
|
||||
LastFound = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, ajax_check_every_seconds, after=partner.calendar_last_notif_ack, mail=False, context=context)
|
||||
if LastFound:
|
||||
for alert in LastFound:
|
||||
all_notif.append(self.do_notif_reminder(cr, uid, alert, context=context))
|
||||
@@ -516,6 +524,7 @@ class calendar_alarm_manager(osv.AbstractModel):
|
||||
cr,
|
||||
uid,
|
||||
[att.id for att in event.attendee_ids],
|
||||
email_from=event.user_id.partner_id.email,
|
||||
template_xmlid='calendar_template_meeting_reminder',
|
||||
force=True,
|
||||
context=context
|
||||
@@ -885,12 +894,12 @@ class calendar_event(osv.Model):
|
||||
stop_date = values.get('stop_date') or event.stop_date
|
||||
start_date = values.get('start_date') or event.start_date
|
||||
if stop_date and start_date:
|
||||
diff = datetime.strptime(stop_date.split(' ')[0], DEFAULT_SERVER_DATE_FORMAT) - datetime.strptime(start_date.split(' ')[0], DEFAULT_SERVER_DATE_FORMAT)
|
||||
diff = openerp.fields.Date.from_string(stop_date) - openerp.fields.Date.from_string(start_date)
|
||||
elif values.get('stop_datetime') or values.get('start_datetime'):
|
||||
stop_datetime = values.get('stop_datetime') or event.stop_datetime
|
||||
start_datetime = values.get('start_datetime') or event.start_datetime
|
||||
if stop_datetime and start_datetime:
|
||||
diff = datetime.strptime(stop_datetime.split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT) - datetime.strptime(start_datetime.split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
diff = openerp.fields.Datetime.from_string(stop_datetime) - openerp.fields.Datetime.from_string(start_datetime)
|
||||
if diff:
|
||||
duration = float(diff.days) * 24 + (float(diff.seconds) / 3600)
|
||||
values['duration'] = round(duration, 2)
|
||||
@@ -1012,7 +1021,7 @@ class calendar_event(osv.Model):
|
||||
tz = pytz.timezone(user.tz) if user.tz else pytz.utc
|
||||
|
||||
if starttime:
|
||||
start = datetime.strptime(starttime.split(' ')[0], DEFAULT_SERVER_DATE_FORMAT)
|
||||
start = openerp.fields.Datetime.from_string(starttime)
|
||||
startdate = tz.localize(start) # Add "+hh:mm" timezone
|
||||
startdate = startdate.replace(hour=8) # Set 8 AM in localtime
|
||||
startdate = startdate.astimezone(pytz.utc) # Convert to UTC
|
||||
@@ -1433,7 +1442,7 @@ class calendar_event(osv.Model):
|
||||
|
||||
def get_interval(self, cr, uid, ids, date, interval, tz=None, context=None):
|
||||
#Function used only in calendar_event_data.xml for email template
|
||||
date = datetime.strptime(date.split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
date = openerp.fields.Datetime.from_string(date)
|
||||
|
||||
if tz:
|
||||
timezone = pytz.timezone(tz or 'UTC')
|
||||
|
||||
@@ -38,39 +38,19 @@
|
||||
<field name="duration" eval="1" />
|
||||
<field name="interval">days</field>
|
||||
<field name="type">notification</field>
|
||||
</record>
|
||||
|
||||
|
||||
</record>
|
||||
<record model="calendar.alarm" id="alarm_mail_1">
|
||||
<field name="name">15 min mail</field>
|
||||
<field name="duration" eval="15" />
|
||||
<field name="interval">minutes</field>
|
||||
<field name="name">2~3 hours mail</field>
|
||||
<field name="duration" eval="3" />
|
||||
<field name="interval">hours</field>
|
||||
<field name="type">email</field>
|
||||
</record>
|
||||
<record model="calendar.alarm" id="alarm_mail_2">
|
||||
<field name="name">30 min mail</field>
|
||||
<field name="duration" eval="30" />
|
||||
<field name="interval">minutes</field>
|
||||
<field name="type">email</field>
|
||||
</record>
|
||||
<record model="calendar.alarm" id="alarm_mail_3">
|
||||
<field name="name">1 hour mail</field>
|
||||
<field name="duration" eval="1" />
|
||||
<field name="name">5~6 hours mail</field>
|
||||
<field name="duration" eval="6" />
|
||||
<field name="interval">hours</field>
|
||||
<field name="type">email</field>
|
||||
</record>
|
||||
<record model="calendar.alarm" id="alarm_mail_4">
|
||||
<field name="name">2 hours mail</field>
|
||||
<field name="duration" eval="2" />
|
||||
<field name="interval">hours</field>
|
||||
<field name="type">email</field>
|
||||
</record>
|
||||
<record model="calendar.alarm" id="alarm_mail_5">
|
||||
<field name="name">1 day mail</field>
|
||||
<field name="duration" eval="1" />
|
||||
<field name="interval">days</field>
|
||||
<field name="type">email</field>
|
||||
</record>
|
||||
|
||||
<record model="calendar.event.type" id="categ_meet1">
|
||||
<field name="name">Customer Meeting</field>
|
||||
|
||||
@@ -21,6 +21,27 @@
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="view_calendar_event_type_tree"/>
|
||||
</record>
|
||||
|
||||
<!-- Calendar Alarm -->
|
||||
<record id="view_calendar_alarm_tree" model="ir.ui.view">
|
||||
<field name="name">Calendar Alarm Tree</field>
|
||||
<field name="model">calendar.alarm</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Calendar Alarm" editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="type"/>
|
||||
<field name="duration"/>
|
||||
<field name="interval"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_calendar_alarm" model="ir.actions.act_window">
|
||||
<field name="name">Calendar Alarm</field>
|
||||
<field name="res_model">calendar.alarm</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="view_calendar_alarm_tree"/>
|
||||
</record>
|
||||
|
||||
<!-- Calendar Events Form View -->
|
||||
|
||||
@@ -226,7 +247,6 @@
|
||||
<calendar string="Meetings" date_start="start" date_stop="stop" date_delay="duration" all_day="allday"
|
||||
display="[name]" color="color_partner_id" attendee="partner_ids" avatar_model="res.partner"
|
||||
use_contacts="True" event_open_popup="%(calendar.view_calendar_event_form_popup)s">
|
||||
|
||||
<field name="name"/>
|
||||
<field name="user_id"/>
|
||||
<field name="color_partner_id"/>
|
||||
@@ -311,10 +331,13 @@
|
||||
<field name="view_id" ref="view_calendar_event_gantt"/>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<menuitem name="Calendar" id="mail_menu_calendar" parent="mail.mail_my_stuff" sequence="10" action="action_calendar_event"/>
|
||||
<menuitem id="menu_calendar_configuration" name="Calendar" parent="base.menu_custom" groups="base.group_no_one"/>
|
||||
<menuitem id="menu_calendar_event_type" parent="menu_calendar_configuration" action="action_calendar_event_type" groups="base.group_no_one"/>
|
||||
|
||||
<menuitem id="menu_calendar_alarm" parent="menu_calendar_configuration" action="action_calendar_alarm" groups="base.group_no_one"/>
|
||||
|
||||
<!-- called in js from '/js/base_calendar.js' -->
|
||||
<record id="action_calendar_event_notify" model="ir.actions.act_window">
|
||||
<field name="name">Meetings</field>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="event_type" model="event.type">
|
||||
<field name="name">Generic Events</field>
|
||||
|
||||
+19
-2
@@ -324,6 +324,11 @@ class stock_quant(osv.osv):
|
||||
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.quant', context=c),
|
||||
}
|
||||
|
||||
def init(self, cr):
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('stock_quant_product_location_index',))
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX stock_quant_product_location_index ON stock_quant (product_id, location_id, company_id, qty, in_date, reservation_id)')
|
||||
|
||||
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True):
|
||||
''' Overwrite the read_group in order to sum the function field 'inventory_value' in group by'''
|
||||
res = super(stock_quant, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby, lazy=lazy)
|
||||
@@ -415,7 +420,12 @@ class stock_quant(osv.osv):
|
||||
self.move_quants_write(cr, uid, to_move_quants, move, location_to, dest_package_id, context=context)
|
||||
self.pool.get('stock.move').recalculate_move_state(cr, uid, to_recompute_move_ids, context=context)
|
||||
if location_to.usage == 'internal':
|
||||
if self.search(cr, uid, [('product_id', '=', move.product_id.id), ('qty','<', 0)], limit=1, context=context):
|
||||
# Do manual search for quant to avoid full table scan (order by id)
|
||||
cr.execute("""
|
||||
SELECT 0 FROM stock_quant, stock_location WHERE product_id = %s AND stock_location.id = stock_quant.location_id AND
|
||||
((stock_location.parent_left >= %s AND stock_location.parent_left < %s) OR stock_location.id = %s) AND qty < 0.0 LIMIT 1
|
||||
""", (move.product_id.id, location_to.parent_left, location_to.parent_right, location_to.id))
|
||||
if cr.fetchone():
|
||||
for quant in quants_reconcile:
|
||||
self._quant_reconcile_negative(cr, uid, quant, move, context=context)
|
||||
|
||||
@@ -529,7 +539,10 @@ class stock_quant(osv.osv):
|
||||
return False
|
||||
qty_round = float_round(qty, precision_rounding=rounding)
|
||||
new_qty_round = float_round(quant.qty - qty, precision_rounding=rounding)
|
||||
new_quant = self.copy(cr, SUPERUSER_ID, quant.id, default={'qty': new_qty_round, 'history_ids': [(4, x.id) for x in quant.history_ids]}, context=context)
|
||||
# Fetch the history_ids manually as it will not do a join with the stock moves then (=> a lot faster)
|
||||
cr.execute("""SELECT move_id FROM stock_quant_move_rel WHERE quant_id = %s""", (quant.id,))
|
||||
res = cr.fetchall()
|
||||
new_quant = self.copy(cr, SUPERUSER_ID, quant.id, default={'qty': new_qty_round, 'history_ids': [(4, x[0]) for x in res]}, context=context)
|
||||
self.write(cr, SUPERUSER_ID, quant.id, {'qty': qty_round}, context=context)
|
||||
return self.browse(cr, uid, new_quant, context=context)
|
||||
|
||||
@@ -1858,6 +1871,10 @@ class stock_move(osv.osv):
|
||||
'You try to move a product using a UoM that is not compatible with the UoM of the product moved. Please use an UoM in the same UoM category.',
|
||||
['product_uom']),
|
||||
]
|
||||
def init(self, cr):
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('stock_move_product_location_index',))
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX stock_move_product_location_index ON stock_move (product_id, location_id, location_dest_id, company_id, state)')
|
||||
|
||||
@api.cr_uid_ids_context
|
||||
def do_unreserve(self, cr, uid, move_ids, context=None):
|
||||
|
||||
@@ -1250,7 +1250,7 @@ instance.web.Client = instance.web.Widget.extend({
|
||||
this.crashmanager = new instance.web.CrashManager();
|
||||
instance.session.on('error', this.crashmanager, this.crashmanager.rpc_error);
|
||||
self.notification = new instance.web.Notification(this);
|
||||
self.notification.appendTo(self.$el);
|
||||
self.notification.appendTo(self.$el.find('.openerp'));
|
||||
self.loading = new instance.web.Loading(self);
|
||||
self.loading.appendTo(self.$('.openerp_webclient_container'));
|
||||
self.action_manager = new instance.web.ActionManager(self);
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
import urlparse
|
||||
import unittest2
|
||||
import urllib2
|
||||
import time
|
||||
import werkzeug.urls
|
||||
|
||||
import lxml.html
|
||||
|
||||
import openerp
|
||||
from openerp import tools
|
||||
import re
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -25,12 +22,15 @@ class Crawler(openerp.tests.HttpCase):
|
||||
post_install = True
|
||||
|
||||
def crawl(self, url, seen=None, msg=''):
|
||||
if seen == None:
|
||||
if seen == None:
|
||||
seen = set()
|
||||
if url in seen:
|
||||
|
||||
url_slug = re.sub(r"[/](([^/=?&]+-)?[0-9]+)([/]|$)", '/<slug>/', url)
|
||||
url_slug = re.sub(r"([^/=?&]+)=[^/=?&]+", '\g<1>=param', url_slug)
|
||||
if url_slug in seen:
|
||||
return seen
|
||||
else:
|
||||
seen.add(url)
|
||||
seen.add(url_slug)
|
||||
|
||||
_logger.info("%s %s", msg, url)
|
||||
r = self.url_open(url)
|
||||
|
||||
@@ -14,18 +14,18 @@ class WebsiteMembership(http.Controller):
|
||||
@http.route([
|
||||
'/members',
|
||||
'/members/page/<int:page>',
|
||||
'/members/association/<int:membership_id>',
|
||||
'/members/association/<int:membership_id>/page/<int:page>',
|
||||
'/members/association/<membership_id>',
|
||||
'/members/association/<membership_id>/page/<int:page>',
|
||||
|
||||
'/members/country/<int:country_id>',
|
||||
'/members/country/<country_name>-<int:country_id>',
|
||||
'/members/country/<int:country_id>/page/<int:page>',
|
||||
'/members/country/<country_name>-<int:country_id>/page/<int:page>',
|
||||
|
||||
'/members/association/<int:membership_id>/country/<country_name>-<int:country_id>',
|
||||
'/members/association/<int:membership_id>/country/<int:country_id>',
|
||||
'/members/association/<int:membership_id>/country/<country_name>-<int:country_id>/page/<int:page>',
|
||||
'/members/association/<int:membership_id>/country/<int:country_id>/page/<int:page>',
|
||||
'/members/association/<membership_id>/country/<country_name>-<int:country_id>',
|
||||
'/members/association/<membership_id>/country/<int:country_id>',
|
||||
'/members/association/<membership_id>/country/<country_name>-<int:country_id>/page/<int:page>',
|
||||
'/members/association/<membership_id>/country/<int:country_id>/page/<int:page>',
|
||||
], type='http', auth="public", website=True)
|
||||
def members(self, membership_id=None, country_name=None, country_id=0, page=0, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
@@ -38,7 +38,8 @@ class WebsiteMembership(http.Controller):
|
||||
|
||||
# base domain for groupby / searches
|
||||
base_line_domain = [("partner.website_published", "=", True), ('state', 'in', ['free', 'paid'])]
|
||||
if membership_id:
|
||||
if membership_id and membership_id != 'free':
|
||||
membership_id = int(membership_id)
|
||||
base_line_domain.append(('membership_id', '=', membership_id))
|
||||
membership = product_obj.browse(cr, uid, membership_id, context=context)
|
||||
else:
|
||||
@@ -48,9 +49,14 @@ class WebsiteMembership(http.Controller):
|
||||
('partner.website_description', 'ilike', post_name)]
|
||||
|
||||
# group by country, based on all customers (base domain)
|
||||
membership_line_ids = membership_line_obj.search(cr, SUPERUSER_ID, base_line_domain, context=context)
|
||||
if membership_id != 'free':
|
||||
membership_line_ids = membership_line_obj.search(cr, SUPERUSER_ID, base_line_domain, context=context)
|
||||
country_domain = ('member_lines', 'in', membership_line_ids)
|
||||
else:
|
||||
membership_line_ids = []
|
||||
country_domain = ('free_member', '=', True)
|
||||
countries = partner_obj.read_group(
|
||||
cr, uid, [('member_lines', 'in', membership_line_ids), ("website_published", "=", True)], ["id", "country_id"],
|
||||
cr, uid, [country_domain, ("website_published", "=", True)], ["id", "country_id"],
|
||||
groupby="country_id", orderby="country_id", context=request.context)
|
||||
countries_total = sum(country_dict['country_id_count'] for country_dict in countries)
|
||||
|
||||
@@ -77,22 +83,38 @@ class WebsiteMembership(http.Controller):
|
||||
line_domain.append(('membership_id', 'in', membership_ids))
|
||||
|
||||
# displayed membership lines
|
||||
membership_line_ids = membership_line_obj.search(cr, SUPERUSER_ID, line_domain, context=context)
|
||||
if membership_id != 'free':
|
||||
membership_line_ids = membership_line_obj.search(cr, SUPERUSER_ID, line_domain, context=context)
|
||||
membership_lines = membership_line_obj.browse(cr, uid, membership_line_ids, context=context)
|
||||
# TODO: Following line can be deleted in master. Kept for retrocompatibility.
|
||||
membership_lines = sorted(membership_lines, key=lambda x: x.membership_id.website_sequence)
|
||||
partner_ids = [m.partner.id for m in membership_lines]
|
||||
free_partner_ids = partner_obj.search(cr, uid, [('free_member', '=', True), ('website_published', '=', True)], context=context)
|
||||
google_map_partner_ids = ",".join(map(str, partner_ids))
|
||||
memberships_data = []
|
||||
for membership_record in memberships:
|
||||
memberships_data.append({'id': membership_record.id, 'name': membership_record.name})
|
||||
memberships_partner_ids = {}
|
||||
for line in membership_lines:
|
||||
memberships_partner_ids.setdefault(line.membership_id.id, []).append(line.partner.id)
|
||||
if free_partner_ids:
|
||||
memberships_data.append({'id': 'free', 'name': _('Free Members')})
|
||||
if not membership_id or membership_id == 'free':
|
||||
memberships_partner_ids['free'] = free_partner_ids
|
||||
|
||||
partners = dict((p.id, p) for p in partner_obj.browse(request.cr, SUPERUSER_ID, partner_ids, request.context))
|
||||
partners = dict((p.id, p) for p in partner_obj.browse(request.cr, SUPERUSER_ID, list(set(partner_ids + free_partner_ids)), request.context))
|
||||
|
||||
# request pager for lines
|
||||
pager = request.website.pager(url="/members", total=len(membership_line_ids), page=page, step=self._references_per_page, scope=7, url_args=post)
|
||||
|
||||
values = {
|
||||
'partners': partners,
|
||||
'membership_lines': membership_lines,
|
||||
'memberships': memberships,
|
||||
'membership': membership,
|
||||
'membership_lines': membership_lines, # TODO: This line can be deleted in master. Kept for retrocompatibility.
|
||||
'memberships': memberships, # TODO: This line too.
|
||||
'membership': membership, # TODO: This line too.
|
||||
'memberships_data': memberships_data,
|
||||
'memberships_partner_ids': memberships_partner_ids,
|
||||
'membership_id': membership_id,
|
||||
'countries': countries,
|
||||
'current_country': current_country and [current_country['id'], current_country['name']] or None,
|
||||
'current_country_id': current_country and current_country['id'] or 0,
|
||||
|
||||
@@ -31,10 +31,10 @@
|
||||
<div class="col-md-3 mb32" id="left_column">
|
||||
<ul class="nav nav-pills nav-stacked mt16">
|
||||
<li class="nav-header"><h3>Associations</h3></li>
|
||||
<li t-att-class="'' if membership else 'active'"><a href="/members">All</a></li>
|
||||
<t t-foreach="memberships" t-as="membership_id">
|
||||
<li t-att-class="membership and membership_id.id == membership.id and 'active' or ''">
|
||||
<a t-attf-href="/members/association/#{ membership_id.id }#{current_country and '/country/%s' % slug(current_country) or ''}#{ search }"><t t-esc="membership_id.name"/></a>
|
||||
<li t-att-class="'' if membership_id else 'active'"><a href="/members">All</a></li>
|
||||
<t t-foreach="memberships_data" t-as="membership_data">
|
||||
<li t-att-class="membership_id and membership_data['id'] == membership_id and 'active' or ''">
|
||||
<a t-attf-href="/members/association/#{ membership_data['id'] }#{current_country and '/country/%s' % slug(current_country) or ''}#{ search }"><t t-esc="membership_data['name']"/></a>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
@@ -51,27 +51,28 @@
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<t t-if="not membership_lines">
|
||||
<t t-if="not memberships_partner_ids">
|
||||
<p>No result found.</p>
|
||||
</t>
|
||||
<t t-foreach="membership_lines" t-as="membership_line_id">
|
||||
<t t-if="current_membership_id != membership_line_id.membership_id.id">
|
||||
<t t-set="current_membership_id" t-value="membership_line_id.membership_id.id"/>
|
||||
<h3 class="text-center"><span t-field="membership_line_id.membership_id"/></h3>
|
||||
<t t-foreach="memberships_data" t-as="membership_data">
|
||||
<t t-if="memberships_partner_ids.get(membership_data['id'])">
|
||||
<h3 class="text-center"><span t-esc="membership_data['name']"/></h3>
|
||||
<t t-foreach="memberships_partner_ids[membership_data['id']]" t-as="partner_id">
|
||||
<t t-set="partner" t-value="partners[partner_id]"/>
|
||||
<div class="media">
|
||||
<a class="pull-left" t-attf-href="/members/#{slug(partner)}"
|
||||
t-field="partner.image_small"
|
||||
t-field-options='{"widget": "image", "class": "media-object"}'
|
||||
></a>
|
||||
<div class="media-body" style="min-height: 64px;">
|
||||
<a class="media-heading" t-attf-href="/members/#{slug(partner)}">
|
||||
<span t-field="partner.display_name"/>
|
||||
</a>
|
||||
<div t-field="partner.website_short_description"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
<t t-set="partner" t-value="partners[membership_line_id.partner.id]"/>
|
||||
<div class="media">
|
||||
<a class="pull-left" t-attf-href="/members/#{slug(partner)}"
|
||||
t-field="partner.image_small"
|
||||
t-field-options='{"widget": "image", "class": "media-object"}'
|
||||
></a>
|
||||
<div class="media-body" style="min-height: 64px;">
|
||||
<a class="media-heading" t-attf-href="/members/#{slug(partner)}">
|
||||
<span t-field="partner.display_name"/>
|
||||
</a>
|
||||
<div t-field="partner.website_short_description"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user