[MERGE] forward port of branch 9.0 up to 24eebbd

This commit is contained in:
Christophe Simonis
2016-05-26 14:06:36 +02:00
27 changed files with 171 additions and 172 deletions
+1 -1
View File
@@ -522,7 +522,7 @@ class AccountInvoice(models.Model):
@api.multi @api.multi
def get_formview_id(self): def get_formview_id(self):
""" Update form view id of action to open the invoice """ """ Update form view id of action to open the invoice """
if self.type == 'in_invoice': if self.type in ('in_invoice', 'in_refund'):
return self.env.ref('account.invoice_supplier_form').id return self.env.ref('account.invoice_supplier_form').id
else: else:
return self.env.ref('account.invoice_form').id return self.env.ref('account.invoice_form').id
+8 -1
View File
@@ -830,7 +830,14 @@ class AccountMoveLine(models.Model):
#if writeoff_acc_id specified, then create write-off move with value the remaining amount from move in self #if writeoff_acc_id specified, then create write-off move with value the remaining amount from move in self
if writeoff_acc_id and writeoff_journal_id and remaining_moves: if writeoff_acc_id and writeoff_journal_id and remaining_moves:
writeoff_to_reconcile = remaining_moves._create_writeoff({'account_id': writeoff_acc_id.id, 'journal_id': writeoff_journal_id.id}) all_aml_share_same_currency = all([x.currency_id == self[0].currency_id for x in self])
writeoff_vals = {
'account_id': writeoff_acc_id.id,
'journal_id': writeoff_journal_id.id
}
if not all_aml_share_same_currency:
writeoff_vals['amount_currency'] = False
writeoff_to_reconcile = remaining_moves._create_writeoff(writeoff_vals)
#add writeoff line to reconcile algo and finish the reconciliation #add writeoff line to reconcile algo and finish the reconciliation
remaining_moves = (remaining_moves + writeoff_to_reconcile).auto_reconcile_lines() remaining_moves = (remaining_moves + writeoff_to_reconcile).auto_reconcile_lines()
+9 -9
View File
@@ -118,12 +118,12 @@
<table style="margin-top: 20px;"><tr> <table style="margin-top: 20px;"><tr>
<td> <td>
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;background:#a24689;padding-top: 4px;"> <div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;background:#a24689;padding-top: 4px;">
${object.event_id.get_interval(object.event_id.start, 'dayname', tz=object.partner_id.tz)} ${object.event_id.get_interval(object.event_id.start, 'dayname', tz=object.partner_id.tz if not object.event_id.allday else None)}
</div> </div>
<div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #F8F8F8;width: 130px;border:1px solid #a24689;"> <div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #F8F8F8;width: 130px;border:1px solid #a24689;">
${object.event_id.get_interval(object.event_id.start,'day', tz=object.partner_id.tz)} ${object.event_id.get_interval(object.event_id.start,'day', tz=object.partner_id.tz if not object.event_id.allday else None)}
</div> </div>
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#a24689'>${object.event_id.get_interval(object.event_id.start, 'month', tz=object.partner_id.tz)}</div> <div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#a24689'>${object.event_id.get_interval(object.event_id.start, 'month', tz=object.partner_id.tz if not object.event_id.allday else None)}</div>
<div style="border-collapse:separate;color: #5F5F5F;text-align:center;width: 130px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid #a24689;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.start, 'time', tz=object.partner_id.tz) or ''}</div> <div style="border-collapse:separate;color: #5F5F5F;text-align:center;width: 130px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid #a24689;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.start, 'time', tz=object.partner_id.tz) or ''}</div>
</td> </td>
<td width="20px;"/> <td width="20px;"/>
@@ -210,12 +210,12 @@
<table style="margin-top: 20px;"><tr> <table style="margin-top: 20px;"><tr>
<td> <td>
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;background:#a24689;padding-top: 4px;"> <div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;background:#a24689;padding-top: 4px;">
${object.event_id.get_interval(object.event_id.start, 'dayname', tz=object.partner_id.tz)} ${object.event_id.get_interval(object.event_id.start, 'dayname', tz=object.partner_id.tz if not object.event_id.allday else None)}
</div> </div>
<div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #F8F8F8;width: 130px;border:1px solid #a24689;"> <div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #F8F8F8;width: 130px;border:1px solid #a24689;">
${object.event_id.get_interval(object.event_id.start,'day', tz=object.partner_id.tz)} ${object.event_id.get_interval(object.event_id.start,'day', tz=object.partner_id.tz if not object.event_id.allday else None)}
</div> </div>
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#a24689'>${object.event_id.get_interval(object.event_id.start, 'month', tz=object.partner_id.tz)}</div> <div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#a24689'>${object.event_id.get_interval(object.event_id.start, 'month', tz=object.partner_id.tz if not object.event_id.allday else None)}</div>
<div style="border-collapse:separate;color: #5F5F5F;text-align:center;width: 130px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid #a24689;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.start, 'time', tz=object.partner_id.tz) or ''}</div> <div style="border-collapse:separate;color: #5F5F5F;text-align:center;width: 130px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid #a24689;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.start, 'time', tz=object.partner_id.tz) or ''}</div>
</td> </td>
<td width="20px;"/> <td width="20px;"/>
@@ -302,12 +302,12 @@
<table style="margin-top: 20px;"><tr> <table style="margin-top: 20px;"><tr>
<td> <td>
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;background:#a24689;padding-top: 4px;"> <div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;background:#a24689;padding-top: 4px;">
${object.event_id.get_interval(object.event_id.start, 'dayname', tz=object.partner_id.tz)} ${object.event_id.get_interval(object.event_id.start, 'dayname', tz=object.partner_id.tz if not object.event_id.allday else None)}
</div> </div>
<div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #F8F8F8;width: 130px;border:1px solid #a24689;"> <div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #F8F8F8;width: 130px;border:1px solid #a24689;">
${object.event_id.get_interval(object.event_id.start,'day', tz=object.partner_id.tz)} ${object.event_id.get_interval(object.event_id.start,'day', tz=object.partner_id.tz if not object.event_id.allday else None)}
</div> </div>
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#a24689'>${object.event_id.get_interval(object.event_id.start, 'month', tz=object.partner_id.tz)}</div> <div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#a24689'>${object.event_id.get_interval(object.event_id.start, 'month', tz=object.partner_id.tz if not object.event_id.allday else None)}</div>
<div style="border-collapse:separate;color: #5F5F5F;text-align:center;width: 130px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid #a24689;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.start, 'time', tz=object.partner_id.tz) or ''}</div> <div style="border-collapse:separate;color: #5F5F5F;text-align:center;width: 130px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid #a24689;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.start, 'time', tz=object.partner_id.tz) or ''}</div>
</td> </td>
<td width="20px;"/> <td width="20px;"/>
+6 -5
View File
@@ -101,18 +101,19 @@
<group> <group>
<field name="start" attrs="{'invisible': True}"/> <field name="start" attrs="{'invisible': True}"/>
<field name="stop" attrs="{'invisible': True}"/> <field name="stop" attrs="{'invisible': True}"/>
<field name="id" attrs="{'invisible': True}"/>
<field name="start_date" string="Starting at" on_change="onchange_dates('start', start_date, stop_date, allday, True)" attrs="{'invisible': [('allday','=',False)]}"/> <field name="start_date" string="Starting at" on_change="onchange_dates('start', start_date, stop_date, allday, True)" attrs="{'invisible': [('allday','=',False)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
<field name="stop_date" string="Ending at" on_change="onchange_dates('stop', start_date, stop_date, allday, True)" attrs="{'invisible': [('allday','=',False)]}"/> <field name="stop_date" string="Ending at" on_change="onchange_dates('stop', start_date, stop_date, allday, True)" attrs="{'invisible': [('allday','=',False)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
<field name="start_datetime" string="Starting at" on_change="onchange_duration(start_datetime, duration)" attrs="{'invisible': [('allday','=',True)]}"/> <field name="start_datetime" string="Starting at" on_change="onchange_duration(start_datetime, duration)" attrs="{'invisible': [('allday','=',True)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
<field name="stop_datetime" invisible="1"/> <field name="stop_datetime" invisible="1"/>
<label for="duration" attrs="{'invisible': [('allday','=',True)]}"/> <label for="duration" attrs="{'invisible': [('allday','=',True)]}"/>
<div attrs="{'invisible': [('allday','=',True)]}"> <div attrs="{'invisible': [('allday','=',True)]}">
<field name="duration" widget="float_time" string="Duration" on_change="onchange_duration(start_datetime, duration)" class="oe_inline"/> <field name="duration" widget="float_time" string="Duration" on_change="onchange_duration(start_datetime, duration)" class="oe_inline" attrs="{'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
<span> hours</span> <span> hours</span>
</div> </div>
<field name="allday" on_change="onchange_allday(start, stop, start_date, stop_date, start_datetime, stop_datetime, allday)"/> <field name="allday" on_change="onchange_allday(start, stop, start_date, stop_date, start_datetime, stop_datetime, allday)" attrs="{'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
</group> </group>
<group> <group>
<field name="categ_ids" widget="many2many_tags" /> <field name="categ_ids" widget="many2many_tags" />
+1 -1
View File
@@ -306,7 +306,7 @@ class MergePartnerAutomatic(osv.TransientModel):
for partner_id in partner_ids: for partner_id in partner_ids:
child_ids = child_ids.union(set(proxy.search(cr, uid, [('id', 'child_of', [partner_id])])) - set([partner_id])) child_ids = child_ids.union(set(proxy.search(cr, uid, [('id', 'child_of', [partner_id])])) - set([partner_id]))
if set(partner_ids).intersection(child_ids): if set(partner_ids).intersection(child_ids):
raise osv.except_osv(_('Error'), _("You cannot merge a contact with one of his parent.")) raise UserError(_("You cannot merge a contact with one of his parent."))
if openerp.SUPERUSER_ID != uid and len(set(partner.email for partner in proxy.browse(cr, uid, partner_ids, context=context))) > 1: if openerp.SUPERUSER_ID != uid and len(set(partner.email for partner in proxy.browse(cr, uid, partner_ids, context=context))) > 1:
raise UserError(_("All contacts must have the same email. Only the Administrator can merge contacts with different emails.")) raise UserError(_("All contacts must have the same email. Only the Administrator can merge contacts with different emails."))
+2 -2
View File
@@ -460,11 +460,11 @@ class crm_lead(format_address, osv.osv):
value = dict(key).get(lead[field_name], lead[field_name]) value = dict(key).get(lead[field_name], lead[field_name])
elif field.type == 'many2one': elif field.type == 'many2one':
if lead[field_name]: if lead[field_name]:
value = lead[field_name].name_get()[0][1] value = lead[field_name].sudo().name_get()[0][1]
elif field.type == 'many2many': elif field.type == 'many2many':
if lead[field_name]: if lead[field_name]:
for val in lead[field_name]: for val in lead[field_name]:
field_value = val.name_get()[0][1] field_value = val.sudo().name_get()[0][1]
value += field_value + "," value += field_value + ","
else: else:
value = lead[field_name] value = lead[field_name]
@@ -3,6 +3,8 @@ access_ crm_lead_report_assign,crm.lead.report.assign,model_crm_lead_report_assi
access_ crm_lead_report_assign_all,crm.lead.report.assign.all,model_crm_lead_report_assign,base.group_user,1,0,0,0 access_ crm_lead_report_assign_all,crm.lead.report.assign.all,model_crm_lead_report_assign,base.group_user,1,0,0,0
access_crm_partner_report,crm.partner.report.assign.all,model_crm_partner_report_assign,base.group_sale_salesman,1,0,0,0 access_crm_partner_report,crm.partner.report.assign.all,model_crm_partner_report_assign,base.group_sale_salesman,1,0,0,0
access_res_partner_grade,res.partner.grade,model_res_partner_grade,base.group_sale_salesman,1,1,1,0 access_res_partner_grade,res.partner.grade,model_res_partner_grade,base.group_sale_salesman,1,1,1,0
access_res_partner_grade_employee,res.partner.grade,model_res_partner_grade,base.group_user,1,0,0,0
access_res_partner_grade_portal,res.partner.grade,model_res_partner_grade,base.group_portal,1,0,0,0
access_res_partner_grade_public,res.partner.grade,model_res_partner_grade,base.group_public,1,0,0,0 access_res_partner_grade_public,res.partner.grade,model_res_partner_grade,base.group_public,1,0,0,0
access_res_partner_grade_manager,res.partner.grade.manager,model_res_partner_grade,base.group_sale_manager,1,1,1,1 access_res_partner_grade_manager,res.partner.grade.manager,model_res_partner_grade,base.group_sale_manager,1,1,1,1
access_res_partner_activation_user,res.partner.activation.user,model_res_partner_activation,base.group_user,1,0,0,0 access_res_partner_activation_user,res.partner.activation.user,model_res_partner_activation,base.group_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
3 access_ crm_lead_report_assign_all crm.lead.report.assign.all model_crm_lead_report_assign base.group_user 1 0 0 0
4 access_crm_partner_report crm.partner.report.assign.all model_crm_partner_report_assign base.group_sale_salesman 1 0 0 0
5 access_res_partner_grade res.partner.grade model_res_partner_grade base.group_sale_salesman 1 1 1 0
6 access_res_partner_grade_employee res.partner.grade model_res_partner_grade base.group_user 1 0 0 0
7 access_res_partner_grade_portal res.partner.grade model_res_partner_grade base.group_portal 1 0 0 0
8 access_res_partner_grade_public res.partner.grade model_res_partner_grade base.group_public 1 0 0 0
9 access_res_partner_grade_manager res.partner.grade.manager model_res_partner_grade base.group_sale_manager 1 1 1 1
10 access_res_partner_activation_user res.partner.activation.user model_res_partner_activation base.group_user 1 0 0 0
+20 -2
View File
@@ -129,18 +129,36 @@ class gamification_goal_definition(osv.Model):
raise UserError(_("The domain for the definition %s seems incorrect, please check it.\n\n%s") % (definition.name, msg)) raise UserError(_("The domain for the definition %s seems incorrect, please check it.\n\n%s") % (definition.name, msg))
return True return True
def _check_model_validity(self, cr, uid, ids, context=None):
""" make sure the selected field and model are usable"""
for definition in self.browse(cr, uid, ids, context=context):
try:
if not definition.model_id or not definition.field_id:
continue
model = self.pool[definition.model_id.model]
field = model._fields[definition.field_id.name]
if not field.store:
raise UserError(
_("The model configuration for the definition %s seems incorrect, please check it.\n\n%s not stored") % (definition.name, definition.field_id.name))
except KeyError, e:
raise UserError(
_("The model configuration for the definition %s seems incorrect, please check it.\n\n%s not found") % (definition.name, e.message))
def create(self, cr, uid, vals, context=None): def create(self, cr, uid, vals, context=None):
res_id = super(gamification_goal_definition, self).create(cr, uid, vals, context=context) res_id = super(gamification_goal_definition, self).create(cr, uid, vals, context=context)
if vals.get('computation_mode') in ('count', 'sum'): if vals.get('computation_mode') in ('count', 'sum'):
self._check_domain_validity(cr, uid, [res_id], context=context) self._check_domain_validity(cr, uid, [res_id], context=context)
if vals.get('field_id'):
self._check_model_validity(cr, uid, [res_id], context=context)
return res_id return res_id
def write(self, cr, uid, ids, vals, context=None): def write(self, cr, uid, ids, vals, context=None):
res = super(gamification_goal_definition, self).write(cr, uid, ids, vals, context=context) res = super(gamification_goal_definition, self).write(cr, uid, ids, vals, context=context)
if vals.get('computation_mode', 'count') in ('count', 'sum') and (vals.get('domain') or vals.get('model_id')): if vals.get('computation_mode', 'count') in ('count', 'sum') and (vals.get('domain') or vals.get('model_id')):
self._check_domain_validity(cr, uid, ids, context=context) self._check_domain_validity(cr, uid, ids, context=context)
if vals.get('field_id') or vals.get('model_id') or vals.get('batch_mode'):
self._check_model_validity(cr, uid, ids, context=context)
return res return res
def on_change_model_id(self, cr, uid, ids, model_id, context=None): def on_change_model_id(self, cr, uid, ids, model_id, context=None):
+2 -2
View File
@@ -567,14 +567,14 @@ class hr_employee(osv.Model):
# Find for holidays status # Find for holidays status
status_ids = type_obj.search(cr, uid, [('limit', '=', False)], context=context) status_ids = type_obj.search(cr, uid, [('limit', '=', False)], context=context)
if len(status_ids) != 1 : if len(status_ids) != 1 :
raise osv.except_osv(_('Warning!'),_("The feature behind the field 'Remaining Legal Leaves' can only be used when there is only one leave type with the option 'Allow to Override Limit' unchecked. (%s Found). Otherwise, the update is ambiguous as we cannot decide on which leave type the update has to be done. \nYou may prefer to use the classic menus 'Leave Requests' and 'Allocation Requests' located in 'Human Resources \ Leaves' to manage the leave days of the employees if the configuration does not allow to use this field.") % (len(status_ids))) raise UserError(_("The feature behind the field 'Remaining Legal Leaves' can only be used when there is only one leave type with the option 'Allow to Override Limit' unchecked. (%s Found). Otherwise, the update is ambiguous as we cannot decide on which leave type the update has to be done. \nYou may prefer to use the classic menus 'Leave Requests' and 'Allocation Requests' located in 'Human Resources \ Leaves' to manage the leave days of the employees if the configuration does not allow to use this field.") % (len(status_ids)))
status_id = status_ids and status_ids[0] or False status_id = status_ids and status_ids[0] or False
if not status_id: if not status_id:
return False return False
if diff > 0: if diff > 0:
leave_id = holiday_obj.create(cr, uid, {'name': _('Allocation for %s') % employee.name, 'employee_id': employee.id, 'holiday_status_id': status_id, 'type': 'add', 'holiday_type': 'employee', 'number_of_days_temp': diff}, context=context) leave_id = holiday_obj.create(cr, uid, {'name': _('Allocation for %s') % employee.name, 'employee_id': employee.id, 'holiday_status_id': status_id, 'type': 'add', 'holiday_type': 'employee', 'number_of_days_temp': diff}, context=context)
elif diff < 0: elif diff < 0:
raise osv.except_osv(_('Warning!'), _('You cannot reduce validated allocation requests')) raise UserError(_('You cannot reduce validated allocation requests'))
else: else:
return False return False
for sig in ('confirm', 'validate', 'second_validate'): for sig in ('confirm', 'validate', 'second_validate'):
@@ -18,13 +18,16 @@ class payslip_details_report(report_sxw.rml_parse):
payslip_line = self.pool.get('hr.payslip.line') payslip_line = self.pool.get('hr.payslip.line')
rule_cate_obj = self.pool.get('hr.salary.rule.category') rule_cate_obj = self.pool.get('hr.salary.rule.category')
def get_recursive_parent(rule_categories): def get_recursive_parent(current_rule_category, rule_categories = None):
if not rule_categories: if rule_categories:
return [] rule_categories = current_rule_category | rule_categories
if rule_categories[0].parent_id: else:
rule_categories = rule_categories[0].parent_id | rule_categories rule_categories = current_rule_category
get_recursive_parent(rule_categories)
return rule_categories if current_rule_category.parent_id:
return get_recursive_parent(current_rule_category.parent_id, rule_categories)
else:
return rule_categories
res = [] res = []
result = {} result = {}
+30 -89
View File
@@ -4,10 +4,10 @@
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 9.0\n" "Project-Id-Version: Odoo Server 9.0c\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-10-09 09:20+0000\n" "POT-Creation-Date: 2016-05-26 11:19+0000\n"
"PO-Revision-Date: 2015-10-09 09:20+0000\n" "PO-Revision-Date: 2016-05-26 11:19+0000\n"
"Last-Translator: <>\n" "Last-Translator: <>\n"
"Language-Team: \n" "Language-Team: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -15,19 +15,6 @@ msgstr ""
"Content-Transfer-Encoding: \n" "Content-Transfer-Encoding: \n"
"Plural-Forms: \n" "Plural-Forms: \n"
#. module: im_livechat
#. openerp-web
#: code:addons/im_livechat/static/src/js/im_livechat.js:222
#, python-format
msgid " for the following reason: %s"
msgstr ""
#. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.loader
msgid "\", {modules:modules, use_cors: false});\n"
" });"
msgstr ""
#. module: im_livechat #. module: im_livechat
#: model:ir.model.fields,field_description:im_livechat.field_im_livechat_report_operator_nbr_channel #: model:ir.model.fields,field_description:im_livechat.field_im_livechat_report_operator_nbr_channel
msgid "# of channel" msgid "# of channel"
@@ -44,20 +31,6 @@ msgstr ""
msgid "% Happy" msgid "% Happy"
msgstr "" msgstr ""
#. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.support_page
msgid "&lt;!DOCTYPE html&gt;"
msgstr ""
#. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.loader
msgid ");\n"
" button.appendTo($('body'));\n"
" window.livechat_button = button;\n"
" });\n"
" });"
msgstr ""
#. module: im_livechat #. module: im_livechat
#: model:ir.model.fields,help:im_livechat.field_im_livechat_channel_rule_action #: model:ir.model.fields,help:im_livechat.field_im_livechat_channel_rule_action
msgid "* Select 'Display the button' to simply display the chat button on the pages.\n" msgid "* Select 'Display the button' to simply display the chat button on the pages.\n"
@@ -87,11 +60,17 @@ msgstr ""
#. module: im_livechat #. module: im_livechat
#. openerp-web #. openerp-web
#: code:addons/im_livechat/static/src/js/im_livechat.js:26 #: code:addons/im_livechat/static/src/js/im_livechat.js:27
#, python-format #, python-format
msgid "Ask something ..." msgid "Ask something ..."
msgstr "" msgstr ""
#. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.mail_channel_view_form
#: model:ir.ui.view,arch_db:im_livechat.mail_channel_view_tree
msgid "Attendees"
msgstr ""
#. module: im_livechat #. module: im_livechat
#: selection:im_livechat.channel.rule,action:0 #: selection:im_livechat.channel.rule,action:0
msgid "Auto popup" msgid "Auto popup"
@@ -176,6 +155,7 @@ msgstr ""
#. module: im_livechat #. module: im_livechat
#: model:ir.model.fields,field_description:im_livechat.field_im_livechat_report_channel_technical_name #: model:ir.model.fields,field_description:im_livechat.field_im_livechat_report_channel_technical_name
#: model:ir.ui.view,arch_db:im_livechat.im_livechat_report_channel_view_search
msgid "Code" msgid "Code"
msgstr "" msgstr ""
@@ -187,7 +167,7 @@ msgstr ""
#. module: im_livechat #. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.im_livechat_channel_view_form #: model:ir.ui.view,arch_db:im_livechat.im_livechat_channel_view_form
msgid "Copy and paste this code into your website, within the &amp;lt;head&amp;gt; tag:" msgid "Copy and paste this code into your website, within the &lt;head&gt; tag:"
msgstr "" msgstr ""
#. module: im_livechat #. module: im_livechat
@@ -290,26 +270,16 @@ msgstr ""
#. module: im_livechat #. module: im_livechat
#. openerp-web #. openerp-web
#: code:addons/im_livechat/static/src/xml/im_livechat.xml:16 #: code:addons/im_livechat/static/src/xml/im_livechat.xml:19
#, python-format #, python-format
msgid "Explain your note" msgid "Explain your note"
msgstr "" msgstr ""
#. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.mail_channel_view_form
msgid "Feedback"
msgstr ""
#. module: im_livechat #. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.im_livechat_channel_view_form #: model:ir.ui.view,arch_db:im_livechat.im_livechat_channel_view_form
msgid "For website built with Odoo CMS, please install the website_livechat module. Then go to Settings &gt; Website Settings and select the Website Live Chat Channel you want to add on your website." msgid "For website built with Odoo CMS, please install the website_livechat module. Then go to Settings &gt; Website Settings and select the Website Live Chat Channel you want to add on your website."
msgstr "" msgstr ""
#. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.mail_channel_view_form
msgid "General"
msgstr ""
#. module: im_livechat #. module: im_livechat
#: model:ir.model.fields,help:im_livechat.field_im_livechat_channel_rule_sequence #: model:ir.model.fields,help:im_livechat.field_im_livechat_channel_rule_sequence
msgid "Given the order to find a matching rule. If 2 rules are matching for the given url/country, the one with the lowest sequence will be chosen." msgid "Given the order to find a matching rule. If 2 rules are matching for the given url/country, the one with the lowest sequence will be chosen."
@@ -361,9 +331,9 @@ msgstr ""
#. module: im_livechat #. module: im_livechat
#. openerp-web #. openerp-web
#: code:addons/im_livechat/static/src/js/im_livechat.js:220 #: code:addons/im_livechat/static/src/xml/im_livechat.xml:15
#, python-format #, python-format
msgid "I rated you with :rating_%d" msgid "I don't want to rate this conversation"
msgstr "" msgstr ""
#. module: im_livechat #. module: im_livechat
@@ -449,11 +419,6 @@ msgstr ""
msgid "Livechat Support Channel Statistics allows you to easily check and analyse your company livechat session performance. Extract informations about the missed sessions, the audiance, the duration of a session, etc." msgid "Livechat Support Channel Statistics allows you to easily check and analyse your company livechat session performance. Extract informations about the missed sessions, the audiance, the duration of a session, etc."
msgstr "" msgstr ""
#. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.support_page
msgid "Livechat Support Page"
msgstr ""
#. module: im_livechat #. module: im_livechat
#: model:ir.model,name:im_livechat.model_im_livechat_report_channel #: model:ir.model,name:im_livechat.model_im_livechat_report_channel
#: model:ir.model,name:im_livechat.model_im_livechat_report_operator #: model:ir.model,name:im_livechat.model_im_livechat_report_operator
@@ -579,6 +544,11 @@ msgstr ""
msgid "Options" msgid "Options"
msgstr "" msgstr ""
#. module: im_livechat
#: model:ir.model.fields,help:im_livechat.field_im_livechat_channel_rating_percentage_satisfaction
msgid "Percentage of happy ratings over the past 7 days"
msgstr ""
#. module: im_livechat #. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.im_livechat_channel_view_kanban #: model:ir.ui.view,arch_db:im_livechat.im_livechat_channel_view_kanban
msgid "Quit" msgid "Quit"
@@ -590,6 +560,13 @@ msgstr ""
msgid "Rating" msgid "Rating"
msgstr "" msgstr ""
#. module: im_livechat
#. openerp-web
#: code:addons/im_livechat/static/src/js/im_livechat.js:308
#, python-format
msgid "Rating: :rating_%d"
msgstr ""
#. module: im_livechat #. module: im_livechat
#: model:ir.model.fields,help:im_livechat.field_im_livechat_channel_rule_regex_url #: model:ir.model.fields,help:im_livechat.field_im_livechat_channel_rule_regex_url
msgid "Regular expression identifying the web page on which the rules will be applied." msgid "Regular expression identifying the web page on which the rules will be applied."
@@ -623,8 +600,9 @@ msgid "Search report"
msgstr "" msgstr ""
#. module: im_livechat #. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.im_livechat_report_channel_view_search #: model:ir.ui.view,arch_db:im_livechat.mail_channel_view_form
msgid "Session" #: model:ir.ui.view,arch_db:im_livechat.mail_channel_view_tree
msgid "Session Date"
msgstr "" msgstr ""
#. module: im_livechat #. module: im_livechat
@@ -777,11 +755,6 @@ msgid "You can create channels for each website on which you want\n"
" visitors to talk in real time with your operators." " visitors to talk in real time with your operators."
msgstr "" msgstr ""
#. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.loader
msgid "document.addEventListener(\"DOMContentLoaded\", function(event) {"
msgstr ""
#. module: im_livechat #. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.im_livechat_channel_view_form #: model:ir.ui.view,arch_db:im_livechat.im_livechat_channel_view_form
msgid "e.g. Hello, how may I help you?" msgid "e.g. Hello, how may I help you?"
@@ -792,30 +765,6 @@ msgstr ""
msgid "e.g. YourWebsite.com" msgid "e.g. YourWebsite.com"
msgstr "" msgstr ""
#. module: im_livechat
#. openerp-web
#: code:addons/im_livechat/static/src/xml/im_livechat_backend.xml:6
#, python-format
msgid "livechat"
msgstr ""
#. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.loader
msgid "odoo.define('im_livechat.livesupport', function (require) {\n"
" var im_livechat = require('im_livechat.im_livechat');\n"
" var button = new im_livechat.LivechatButton(\n"
" $('body'),\n"
" \""
msgstr ""
#. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.loader
msgid "odoo.define('web.session', function (require) {\n"
" var Session = require('web.Session');\n"
" var modules = odoo._modules;\n"
" return new Session(undefined, \""
msgstr ""
#. module: im_livechat #. module: im_livechat
#: model:ir.ui.view,arch_db:im_livechat.im_livechat_channel_view_form #: model:ir.ui.view,arch_db:im_livechat.im_livechat_channel_view_form
msgid "or copy this url and send it by email to your customers or suppliers:" msgid "or copy this url and send it by email to your customers or suppliers:"
@@ -826,11 +775,3 @@ msgstr ""
msgid "seconds" msgid "seconds"
msgstr "" msgstr ""
#. module: im_livechat
#. openerp-web
#: code:addons/im_livechat/static/src/xml/im_livechat_backend.xml:9
#: code:addons/im_livechat/static/src/xml/im_livechat_backend.xml:12
#, python-format
msgid "true"
msgstr ""
+1 -1
View File
@@ -1598,7 +1598,7 @@ class product_template(osv.osv):
product_ctx = dict(context or {}, active_test=False) product_ctx = dict(context or {}, active_test=False)
if self.search_count(cr, uid, [('id', 'in', ids), ('available_in_pos', '=', True)], context=product_ctx): if self.search_count(cr, uid, [('id', 'in', ids), ('available_in_pos', '=', True)], context=product_ctx):
if self.pool['pos.session'].search_count(cr, uid, [('state', '!=', 'closed')], context=context): if self.pool['pos.session'].search_count(cr, uid, [('state', '!=', 'closed')], context=context):
raise osv.except_osv(_('Error!'), raise UserError(
_('You cannot delete a product saleable in point of sale while a session is still opened.')) _('You cannot delete a product saleable in point of sale while a session is still opened.'))
return super(product_template, self).unlink(cr, uid, ids, context=context) return super(product_template, self).unlink(cr, uid, ids, context=context)
@@ -131,6 +131,8 @@ var ScreenWidget = PosBaseWidget.extend({
this.pos.barcode_reader.set_action_callback({ this.pos.barcode_reader.set_action_callback({
'cashier': _.bind(self.barcode_cashier_action, self), 'cashier': _.bind(self.barcode_cashier_action, self),
'product': _.bind(self.barcode_product_action, self), 'product': _.bind(self.barcode_product_action, self),
'weight': _.bind(self.barcode_product_action, self),
'price': _.bind(self.barcode_product_action, self),
'client' : _.bind(self.barcode_client_action, self), 'client' : _.bind(self.barcode_client_action, self),
'discount': _.bind(self.barcode_discount_action, self), 'discount': _.bind(self.barcode_discount_action, self),
'error' : _.bind(self.barcode_error_action, self), 'error' : _.bind(self.barcode_error_action, self),
@@ -33,14 +33,15 @@ class sale_order_line(osv.osv):
currency_id = item.pricelist_id.currency_id currency_id = item.pricelist_id.currency_id
product = product_obj.browse(cr, uid, product_id, context=context) product = product_obj.browse(cr, uid, product_id, context=context)
product_currency = (product.company_id and product.company_id.currency_id) or self.pool['res.users'].browse(cr, uid, uid).company_id.currency_id
if not currency_id: if not currency_id:
currency_id = product.company_id.currency_id currency_id = product_currency
cur_factor = 1.0 cur_factor = 1.0
else: else:
if currency_id.id == product.company_id.currency_id.id: if currency_id.id == product_currency.id:
cur_factor = 1.0 cur_factor = 1.0
else: else:
cur_factor = self.pool['res.currency']._get_conversion_rate(cr, uid, product.company_id.currency_id, currency_id, context=context) cur_factor = self.pool['res.currency']._get_conversion_rate(cr, uid, product_currency, currency_id, context=context)
product_uom = context and context.get('uom') or product.uom_id.id product_uom = context and context.get('uom') or product.uom_id.id
if uom and uom != product_uom: if uom and uom != product_uom:
+1 -1
View File
@@ -50,7 +50,7 @@ except (OSError, IOError):
else: else:
_logger.info('Will use the Wkhtmltopdf binary at %s' % _get_wkhtmltopdf_bin()) _logger.info('Will use the Wkhtmltopdf binary at %s' % _get_wkhtmltopdf_bin())
out, err = process.communicate() out, err = process.communicate()
version = re.search('([0-9.]+)', out).group(0) version = re.search('([0-9.]+)', out or "0").group(0)
if LooseVersion(version) < LooseVersion('0.12.0'): if LooseVersion(version) < LooseVersion('0.12.0'):
_logger.info('Upgrade Wkhtmltopdf to (at least) 0.12.0') _logger.info('Upgrade Wkhtmltopdf to (at least) 0.12.0')
wkhtmltopdf_state = 'upgrade' wkhtmltopdf_state = 'upgrade'
+3 -11
View File
@@ -544,17 +544,9 @@ class SaleOrderLine(models.Model):
def _compute_tax_id(self): def _compute_tax_id(self):
for line in self: for line in self:
fpos = line.order_id.fiscal_position_id or line.order_id.partner_id.property_account_position_id fpos = line.order_id.fiscal_position_id or line.order_id.partner_id.property_account_position_id
if fpos: # If company_id is set, always filter taxes by the company
# The superuser is used by website_sale in order to create a sale order. We need to make taxes = line.product_id.taxes_id.filtered(lambda r: not line.company_id or r.company_id == line.company_id)
# sure we only select the taxes related to the company of the partner. This should only line.tax_id = fpos.map_tax(taxes) if fpos else taxes
# apply if the partner is linked to a company.
if self.env.uid == SUPERUSER_ID and line.order_id.company_id:
taxes = fpos.map_tax(line.product_id.taxes_id).filtered(lambda r: r.company_id == line.order_id.company_id)
else:
taxes = fpos.map_tax(line.product_id.taxes_id)
line.tax_id = taxes
else:
line.tax_id = line.product_id.taxes_id if line.product_id.taxes_id else False
@api.multi @api.multi
def _prepare_order_line_procurement(self, group_id=False): def _prepare_order_line_procurement(self, group_id=False):
+1 -1
View File
@@ -45,7 +45,7 @@ class stock_return_picking(osv.osv_memory):
context = {} context = {}
if context and context.get('active_ids', False): if context and context.get('active_ids', False):
if len(context.get('active_ids')) > 1: if len(context.get('active_ids')) > 1:
raise osv.except_osv(_('Warning!'), _("You may only return one picking at a time!")) raise UserError(_("You may only return one picking at a time!"))
res = super(stock_return_picking, self).default_get(cr, uid, fields, context=context) res = super(stock_return_picking, self).default_get(cr, uid, fields, context=context)
record_id = context and context.get('active_id', False) or False record_id = context and context.get('active_id', False) or False
uom_obj = self.pool.get('product.uom') uom_obj = self.pool.get('product.uom')
+5 -3
View File
@@ -255,7 +255,7 @@ class stock_quant(osv.osv):
:param context: context dictionary that can explicitly mention the company to consider via the 'force_company' key :param context: context dictionary that can explicitly mention the company to consider via the 'force_company' key
:returns: journal_id, source account, destination account, valuation account :returns: journal_id, source account, destination account, valuation account
:raise: osv.except_osv() is any mandatory account or journal is not defined. :raise: openerp.exceptions.UserError if any mandatory account or journal is not defined.
""" """
product_obj = self.pool.get('product.template') product_obj = self.pool.get('product.template')
accounts = product_obj.browse(cr, uid, move.product_id.product_tmpl_id.id, context).get_product_accounts() accounts = product_obj.browse(cr, uid, move.product_id.product_tmpl_id.id, context).get_product_accounts()
@@ -432,12 +432,14 @@ class stock_move(osv.osv):
else: else:
tmpl_dict[product_id] = 0 tmpl_dict[product_id] = 0
product_avail = qty_available product_avail = qty_available
# if the incoming move is for a purchase order with foreign currency, need to call this to get the same value that the quant will use.
price_unit = self.pool.get('stock.move').get_price_unit(cr, uid, move, context=context)
if product_avail <= 0: if product_avail <= 0:
new_std_price = move.price_unit new_std_price = price_unit
else: else:
# Get the standard price # Get the standard price
amount_unit = product.standard_price amount_unit = product.standard_price
new_std_price = ((amount_unit * product_avail) + (move.price_unit * move.product_qty)) / (product_avail + move.product_qty) new_std_price = ((amount_unit * product_avail) + (price_unit * move.product_qty)) / (product_avail + move.product_qty)
tmpl_dict[product_id] += move.product_qty tmpl_dict[product_id] += move.product_qty
# Write the standard price, as SUPERUSER_ID because a warehouse manager may not have the right to write on products # Write the standard price, as SUPERUSER_ID because a warehouse manager may not have the right to write on products
ctx = dict(context or {}, force_company=move.company_id.id) ctx = dict(context or {}, force_company=move.company_id.id)
+1 -1
View File
@@ -792,7 +792,7 @@
<span class="o_count"/> <span class="o_count"/>
<button class="btn btn-link" type="button" <button class="btn btn-link" type="button"
t-att-accesskey="widget.node.attrs.accesskey"/> t-att-accesskey="widget.node.attrs.accesskey"/>
<input t-if="widget.debug" type="text" class="o_form_input o_debug_input"/> <input t-if="widget.debug" t-att-readonly="widget.get('effective_readonly')" type="text" class="o_form_input o_debug_input"/>
</div> </div>
</t> </t>
<t t-name="web.datepicker"> <t t-name="web.datepicker">
@@ -4,5 +4,5 @@
<field name="name">Dashboard</field> <field name="name">Dashboard</field>
<field name="tag">web_settings_dashboard.main</field> <field name="tag">web_settings_dashboard.main</field>
</record> </record>
<menuitem name="Dashboard" id="web_dashboard_menu" action="web_settings_dashboard_action" parent="base.menu_administration" sequence="-1" /> <menuitem name="Dashboard" id="web_dashboard_menu" action="web_settings_dashboard_action" parent="base.menu_administration" sequence="-1" groups="base.group_system"/>
</odoo> </odoo>
@@ -20,7 +20,7 @@ if(!$('.website_forum').length) {
ev.preventDefault(); ev.preventDefault();
var $warning = $('<div class="alert alert-danger alert-dismissable oe_forum_alert" id="karma_alert">'+ var $warning = $('<div class="alert alert-danger alert-dismissable oe_forum_alert" id="karma_alert">'+
'<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">&times;</button>'+ '<button type="button" class="close notification_close" data-dismiss="alert" aria-hidden="true">&times;</button>'+
karma + _t(' karma is required to perform this action. You can earn karma by having your answers upvoted by the community.') + '</div>'); karma + ' ' + _t(' karma is required to perform this action. You can earn karma by having your answers upvoted by the community.') + '</div>');
var vote_alert = $(ev.currentTarget).parent().find("#vote_alert"); var vote_alert = $(ev.currentTarget).parent().find("#vote_alert");
if (vote_alert.length == 0) { if (vote_alert.length == 0) {
$(ev.currentTarget).parent().append($warning); $(ev.currentTarget).parent().append($warning);
@@ -84,6 +84,14 @@
<t t-set="object" t-value="message"/> <t t-set="object" t-value="message"/>
<t t-call="website.publish_short"/> <t t-call="website.publish_short"/>
<div t-field="message.body"/> <div t-field="message.body"/>
<div class="o_mg_link_content">
<div class="col-md-2 col-sm-3 text-center" t-foreach='message.attachment_ids' t-as='attachment'>
<a t-attf-href="/web/content/#{attachment.id}?download=true" target="_blank">
<div class='oe_attachment_embedded o_image' t-att-title="attachment.name" t-att-data-mimetype="attachment.mimetype" t-attf-data-src="/web/image/#{attachment.id}/100x80"/>
<div class='oe_attachment_name'><t t-raw='attachment.name' /></div>
</a>
</div>
</div>
</div> </div>
</div> </div>
</li> </li>
@@ -53,6 +53,7 @@
<div class="col-md-12"> <div class="col-md-12">
<h4> <h4>
Issue <span t-field="issue.id"/> - <span t-field="issue.name"/> Issue <span t-field="issue.id"/> - <span t-field="issue.name"/>
<a class="btn btn-info" t-att-href="'/web#return_label=Website&amp;model=project.issue&amp;id=%s&amp;view_type=form' % (issue.id)" groups="project.group_project_user">Edit Issue</a>
<span t-field="issue.stage_id.name" class="pull-right label label-info" title="Current stage of this issue"/> <span t-field="issue.stage_id.name" class="pull-right label label-info" title="Current stage of this issue"/>
</h4> </h4>
</div> </div>
+15 -15
View File
@@ -9473,18 +9473,18 @@ h6,
line-height: 1.4; line-height: 1.4;
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
.has_code_col h1, .has_code_col .doc-aside h1,
.has_code_col h2, .has_code_col .doc-aside h2,
.has_code_col h3, .has_code_col .doc-aside h3,
.has_code_col h4, .has_code_col .doc-aside h4,
.has_code_col h5, .has_code_col .doc-aside h5,
.has_code_col h6, .has_code_col .doc-aside h6,
.has_code_col .h1, .has_code_col .doc-aside .h1,
.has_code_col .h2, .has_code_col .doc-aside .h2,
.has_code_col .h3, .has_code_col .doc-aside .h3,
.has_code_col .h4, .has_code_col .doc-aside .h4,
.has_code_col .h5, .has_code_col .doc-aside .h5,
.has_code_col .h6 { .has_code_col .doc-aside .h6 {
color: white; color: white;
} }
} }
@@ -11928,7 +11928,7 @@ main.index.animating .card {
font-size: 65%; font-size: 65%;
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
.has_code_col .card.top .container h1 { .has_code_col .doc-aside .card.top .container h1 {
color: white; color: white;
} }
} }
@@ -11969,7 +11969,7 @@ main.index.animating .card {
} }
} }
@media screen and (min-width: 768px) and (min-width: 1200px) { @media screen and (min-width: 768px) and (min-width: 1200px) {
.has_code_col .card.top .container h1 { .has_code_col .doc-aside .card.top .container h1 {
color: white; color: white;
} }
} }
@@ -12010,7 +12010,7 @@ main.index.animating .card {
font-size: 65%; font-size: 65%;
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
.has_code_col .toctree-wrapper > ul > li.toctree-l1 > span { .has_code_col .doc-aside .toctree-wrapper > ul > li.toctree-l1 > span {
color: white; color: white;
} }
} }
+15
View File
@@ -0,0 +1,15 @@
United States, 2016-05-25
Rock Solid Solutions, LLC <http://www.rocksolidsolutions.org> agrees to the terms of the Odoo Corporate Contributor License
Agreement v1.0.
I declare that I am authorized and able to make this agreement and sign this
declaration.
Signed,
Daniel Kauffman, Manager rocksolidsolutions@users.noreply.github.com <https://github.com/rocksolidsolutions>
List of contributors:
Daniel Kauffman rocksolidsolutions@users.noreply.github.com <https://github.com/rocksolidsolutions>
+1 -1
View File
@@ -517,7 +517,7 @@ class res_users(osv.osv):
:return: True :return: True
:raise: openerp.exceptions.AccessDenied when old password is wrong :raise: openerp.exceptions.AccessDenied when old password is wrong
:raise: except_osv when new password is not set or empty :raise: openerp.exceptions.UserError when new password is not set or empty
""" """
self.check(cr.dbname, uid, old_passwd) self.check(cr.dbname, uid, old_passwd)
if new_passwd: if new_passwd:
+20 -14
View File
@@ -383,7 +383,8 @@ class PreforkServer(CommonServer):
""" """
def __init__(self, app): def __init__(self, app):
# config # config
self.address = (config['xmlrpc_interface'] or '0.0.0.0', config['xmlrpc_port']) self.address = config['xmlrpc'] and \
(config['xmlrpc_interface'] or '0.0.0.0', config['xmlrpc_port'])
self.population = config['workers'] self.population = config['workers']
self.timeout = config['limit_time_real'] self.timeout = config['limit_time_real']
self.limit_request = config['limit_request'] self.limit_request = config['limit_request']
@@ -558,12 +559,13 @@ class PreforkServer(CommonServer):
signal.signal(signal.SIGQUIT, dumpstacks) signal.signal(signal.SIGQUIT, dumpstacks)
signal.signal(signal.SIGUSR1, log_ormcache_stats) signal.signal(signal.SIGUSR1, log_ormcache_stats)
# listen to socket if self.address:
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # listen to socket
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setblocking(0) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.address) self.socket.setblocking(0)
self.socket.listen(8 * self.population) self.socket.bind(self.address)
self.socket.listen(8 * self.population)
def stop(self, graceful=True): def stop(self, graceful=True):
if self.long_polling_pid is not None: if self.long_polling_pid is not None:
@@ -587,7 +589,8 @@ class PreforkServer(CommonServer):
_logger.info("Stopping forcefully") _logger.info("Stopping forcefully")
for pid in self.workers.keys(): for pid in self.workers.keys():
self.worker_kill(pid, signal.SIGTERM) self.worker_kill(pid, signal.SIGTERM)
self.socket.close() if self.socket:
self.socket.close()
def run(self, preload, stop): def run(self, preload, stop):
self.start() self.start()
@@ -690,11 +693,13 @@ class Worker(object):
_logger.info("Worker %s (%s) alive", self.__class__.__name__, self.pid) _logger.info("Worker %s (%s) alive", self.__class__.__name__, self.pid)
# Reseed the random number generator # Reseed the random number generator
random.seed() random.seed()
# Prevent fd inherientence close_on_exec if self.multi.socket:
flags = fcntl.fcntl(self.multi.socket, fcntl.F_GETFD) | fcntl.FD_CLOEXEC # Prevent fd inheritance: close_on_exec
fcntl.fcntl(self.multi.socket, fcntl.F_SETFD, flags) flags = fcntl.fcntl(self.multi.socket, fcntl.F_GETFD) | fcntl.FD_CLOEXEC
# reset blocking status fcntl.fcntl(self.multi.socket, fcntl.F_SETFD, flags)
self.multi.socket.setblocking(0) # reset blocking status
self.multi.socket.setblocking(0)
signal.signal(signal.SIGINT, self.signal_handler) signal.signal(signal.SIGINT, self.signal_handler)
signal.signal(signal.SIGTERM, signal.SIG_DFL) signal.signal(signal.SIGTERM, signal.SIG_DFL)
signal.signal(signal.SIGCHLD, signal.SIG_DFL) signal.signal(signal.SIGCHLD, signal.SIG_DFL)
@@ -812,7 +817,8 @@ class WorkerCron(Worker):
def start(self): def start(self):
os.nice(10) # mommy always told me to be nice with others... os.nice(10) # mommy always told me to be nice with others...
Worker.start(self) Worker.start(self)
self.multi.socket.close() if self.multi.socket:
self.multi.socket.close()
#---------------------------------------------------------- #----------------------------------------------------------
# start/stop public api # start/stop public api