[MERGE] forward port branch saas-12 up to 0ca99ff

This commit is contained in:
Christophe Simonis
2016-11-02 17:56:37 +01:00
11 changed files with 49 additions and 22 deletions
@@ -645,8 +645,9 @@ class AccountBankStatementLine(models.Model):
# Look for a single move line with the same amount # Look for a single move line with the same amount
field = currency and 'amount_residual_currency' or 'amount_residual' field = currency and 'amount_residual_currency' or 'amount_residual'
liquidity_field = currency and 'amount_currency' or amount > 0 and 'debit' or 'credit' liquidity_field = currency and 'amount_currency' or amount > 0 and 'debit' or 'credit'
liquidity_amt_clause = currency and '%(amount)s' or 'abs(%(amount)s)'
sql_query = self._get_common_sql_query(excluded_ids=excluded_ids) + \ sql_query = self._get_common_sql_query(excluded_ids=excluded_ids) + \
" AND ("+field+" = %(amount)s OR (acc.internal_type = 'liquidity' AND "+liquidity_field+" = %(amount)s)) \ " AND ("+field+" = %(amount)s OR (acc.internal_type = 'liquidity' AND "+liquidity_field+" = " + liquidity_amt_clause + ")) \
ORDER BY date_maturity asc, aml.id asc LIMIT 1" ORDER BY date_maturity asc, aml.id asc LIMIT 1"
self.env.cr.execute(sql_query, params) self.env.cr.execute(sql_query, params)
results = self.env.cr.fetchone() results = self.env.cr.fetchone()
+4 -2
View File
@@ -218,9 +218,10 @@ class HrExpense(models.Model):
raise UserError(_("No Home Address found for the employee %s, please configure one.") % (expense.employee_id.name)) raise UserError(_("No Home Address found for the employee %s, please configure one.") % (expense.employee_id.name))
emp_account = expense.employee_id.address_home_id.property_account_payable_id.id emp_account = expense.employee_id.address_home_id.property_account_payable_id.id
aml_name = expense.employee_id.name + ': ' + expense.name.split('\n')[0][:64]
move_lines.append({ move_lines.append({
'type': 'dest', 'type': 'dest',
'name': expense.employee_id.name, 'name': aml_name,
'price': total, 'price': total,
'account_id': emp_account, 'account_id': emp_account,
'date_maturity': acc_date, 'date_maturity': acc_date,
@@ -253,9 +254,10 @@ class HrExpense(models.Model):
if not account: if not account:
raise UserError(_('Please configure Default Expense account for Product expense: `property_account_expense_categ_id`.')) raise UserError(_('Please configure Default Expense account for Product expense: `property_account_expense_categ_id`.'))
aml_name = expense.employee_id.name + ': ' + expense.name.split('\n')[0][:64]
move_line = { move_line = {
'type': 'src', 'type': 'src',
'name': expense.name.split('\n')[0][:64], 'name': aml_name,
'price_unit': expense.unit_amount, 'price_unit': expense.unit_amount,
'quantity': expense.quantity, 'quantity': expense.quantity,
'price': expense.total_amount, 'price': expense.total_amount,
@@ -141,6 +141,7 @@
<field name="uom_id" ref="product.product_uom_kgm" /> <field name="uom_id" ref="product.product_uom_kgm" />
<field name="uom_po_id" ref="product.product_uom_kgm" /> <field name="uom_po_id" ref="product.product_uom_kgm" />
<field name="image" type="base64" file="point_of_sale/static/img/peche-image.jpg"/> <field name="image" type="base64" file="point_of_sale/static/img/peche-image.jpg"/>
<field name="taxes_id" eval='[(5,)]'/> <!-- no taxes -->
</record> </record>
<record id="raisins_noir" model="product.product"> <record id="raisins_noir" model="product.product">
+4 -1
View File
@@ -4,6 +4,7 @@
from datetime import timedelta from datetime import timedelta
from odoo import api, fields, models from odoo import api, fields, models
from odoo.tools.safe_eval import safe_eval
class ProjectTaskType(models.Model): class ProjectTaskType(models.Model):
@@ -104,7 +105,9 @@ class Project(models.Model):
def action_view_task_rating(self): def action_view_task_rating(self):
""" return the action to see all the rating about the tasks of the project """ """ return the action to see all the rating about the tasks of the project """
action = self.env['ir.actions.act_window'].for_xml_id('rating', 'action_view_rating') action = self.env['ir.actions.act_window'].for_xml_id('rating', 'action_view_rating')
return dict(action, domain=[('res_id', 'in', self.tasks.ids), ('res_model', '=', 'project.task')]) action_domain = safe_eval(action['domain']) if action['domain'] else []
domain = action_domain + [('res_id', 'in', self.tasks.ids), ('res_model', '=', 'project.task')]
return dict(action, domain=domain)
@api.multi @api.multi
def action_view_all_rating(self): def action_view_all_rating(self):
@@ -4,6 +4,7 @@
from datetime import timedelta from datetime import timedelta
from odoo import api, fields, models from odoo import api, fields, models
from odoo.tools.safe_eval import safe_eval
class ProjectIssue(models.Model): class ProjectIssue(models.Model):
@@ -28,7 +29,6 @@ class ProjectIssue(models.Model):
return super(ProjectIssue, self).rating_apply(rate, token=token, feedback=feedback, subtype="rating_project_issue.mt_issue_rating") return super(ProjectIssue, self).rating_apply(rate, token=token, feedback=feedback, subtype="rating_project_issue.mt_issue_rating")
class Stage(models.Model): class Stage(models.Model):
_inherit = ['project.task.type'] _inherit = ['project.task.type']
@@ -45,7 +45,7 @@ class Project(models.Model):
def _send_rating_mail(self): def _send_rating_mail(self):
super(Project, self)._send_rating_mail() super(Project, self)._send_rating_mail()
for project in self: for project in self:
project.issue_ids._send_issue_rating_mail() self.env['project.issue'].search([('project_id', '=', project.id)])._send_issue_rating_mail()
@api.depends('percentage_satisfaction_task', 'percentage_satisfaction_issue') @api.depends('percentage_satisfaction_task', 'percentage_satisfaction_issue')
def _compute_percentage_satisfaction_project(self): def _compute_percentage_satisfaction_project(self):
@@ -58,7 +58,7 @@ class Project(models.Model):
activity_great = activity_task['great'] activity_great = activity_task['great']
activity_sum = sum(activity_task.values()) activity_sum = sum(activity_task.values())
if project.use_issues: if project.use_issues:
activity_issue = project.issue_ids.rating_get_grades(domain) activity_issue = self.env['project.issue'].search([('project_id', '=', project.id)]).rating_get_grades(domain)
activity_great += activity_issue['great'] activity_great += activity_issue['great']
activity_sum += sum(activity_issue.values()) activity_sum += sum(activity_issue.values())
project.percentage_satisfaction_project = activity_great * 100 / activity_sum if activity_sum else -1 project.percentage_satisfaction_project = activity_great * 100 / activity_sum if activity_sum else -1
@@ -77,7 +77,9 @@ class Project(models.Model):
""" return the action to see all the rating about the issues of the project """ """ return the action to see all the rating about the issues of the project """
action = self.env['ir.actions.act_window'].for_xml_id('rating', 'action_view_rating') action = self.env['ir.actions.act_window'].for_xml_id('rating', 'action_view_rating')
issues = self.env['project.issue'].search([('project_id', 'in', self.ids)]) issues = self.env['project.issue'].search([('project_id', 'in', self.ids)])
return dict(action, domain=[('res_id', 'in', issues.ids), ('res_model', '=', 'project.issue')]) action_domain = safe_eval(action['domain']) if action['domain'] else []
domain = action_domain + [('res_id', 'in', issues.ids), ('res_model', '=', 'project.issue')]
return dict(action, domain=domain)
@api.multi @api.multi
def action_view_all_rating(self): def action_view_all_rating(self):
+9
View File
@@ -108,6 +108,15 @@ class PickingType(models.Model):
res.append((picking_type.id, name)) res.append((picking_type.id, name))
return res return res
@api.model
def name_search(self, name, args=None, operator='ilike', limit=100):
args = args or []
domain = []
if name:
domain = ['|', ('name', operator, name), ('warehouse_id.name', operator, name)]
picks = self.search(domain + args, limit=limit)
return picks.name_get()
@api.onchange('code') @api.onchange('code')
def onchange_picking_code(self): def onchange_picking_code(self):
if self.code == 'incoming': if self.code == 'incoming':
+10 -7
View File
@@ -2302,13 +2302,16 @@ eventHandler.modules.popover.update = function ($popover, oStyle, isAirMode) {
} }
}; };
options.onPaste = function (e) { // override summernote clipboard functionality
e.preventDefault(); // FIXME this does not work with our custom out-dated summernote. eventHandler.modules.clipboard.attach = function(layoutInfo) {
document.execCommand("undo"); // FIXME ... so use this hack until we decide to do something about it. var $editable = layoutInfo.editable();
$editable.on('paste', function(e) {
var pastedText = ((e.originalEvent || e).clipboardData || window.clipboardData).getData('Text'); e.preventDefault();
var formattedText = pastedText.replace(/([^.!?:;])\r?\n/g, "$1").trim(); // Remove linebreaks which are not at the end of a sentence $editable.data('NoteHistory').recordUndo($editable);
document.execCommand("insertText", false, formattedText); var pastedText = ((e.originalEvent || e).clipboardData || window.clipboardData).getData('Text');
var formattedText = pastedText.replace(/([^.!?:;])\r?\n/g, "$1").trim(); // Remove linebreaks which are not at the end of a sentence
document.execCommand("insertText", false, formattedText);
});
}; };
////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////
+2 -3
View File
@@ -166,14 +166,13 @@ class Http(models.AbstractModel):
langs = [lg[0] for lg in request.website.get_languages()] langs = [lg[0] for lg in request.website.get_languages()]
path = request.httprequest.path.split('/') path = request.httprequest.path.split('/')
if first_pass: if first_pass:
is_a_bot = cls.is_a_bot()
nearest_lang = not func and cls.get_nearest_lang(path[1]) nearest_lang = not func and cls.get_nearest_lang(path[1])
url_lang = nearest_lang and path[1] url_lang = nearest_lang and path[1]
preferred_lang = ((cook_lang if cook_lang in langs else False) preferred_lang = ((cook_lang if cook_lang in langs else False)
or cls.get_nearest_lang(request.lang) or (not is_a_bot and cls.get_nearest_lang(request.lang))
or request.website.default_lang_code) or request.website.default_lang_code)
is_a_bot = cls.is_a_bot()
request.lang = context['lang'] = nearest_lang or preferred_lang request.lang = context['lang'] = nearest_lang or preferred_lang
# if lang in url but not the displayed or default language --> change or remove # if lang in url but not the displayed or default language --> change or remove
# or no lang in url, and lang to dispay not the default language --> add lang # or no lang in url, and lang to dispay not the default language --> add lang
@@ -241,7 +241,7 @@
<h4 class="text-center text-muted" <h4 class="text-center text-muted"
t-field="event.address_id" t-options='{ t-field="event.address_id" t-options='{
"widget": "contact", "widget": "contact",
"fields": ["address", "phone", "mobile", "fax"] "fields": ["name", "address", "phone", "mobile", "fax"]
}'/> }'/>
</t> </t>
</template> </template>
@@ -342,7 +342,7 @@
</t> </t>
<div itemprop="location" class="mt16 mb8" t-field="event.address_id" t-options='{ <div itemprop="location" class="mt16 mb8" t-field="event.address_id" t-options='{
"widget": "contact", "widget": "contact",
"fields": ["address", "phone", "mobile", "fax", "email"] "fields": ["name", "address", "phone", "mobile", "fax", "email"]
}'/> }'/>
</div> </div>
</div> </div>
@@ -521,7 +521,7 @@
<i class="fa fa-clock-o"/> <span itemprop="startDate" t-esc="event.date_begin_located[:-3]"> </span> <i>to</i> <span itemprop="endDate" t-esc="event.date_end_located[:-3]"> </span> <i class="fa fa-clock-o"/> <span itemprop="startDate" t-esc="event.date_begin_located[:-3]"> </span> <i>to</i> <span itemprop="endDate" t-esc="event.date_end_located[:-3]"> </span>
<div itemprop="location" class="mt16 mb8" t-field="event.address_id" t-options='{ <div itemprop="location" class="mt16 mb8" t-field="event.address_id" t-options='{
"widget": "contact", "widget": "contact",
"fields": ["address", "phone", "mobile", "fax", "email"] "fields": ["name", "address", "phone", "mobile", "fax", "email"]
}'/> }'/>
</div> </div>
<div class="col-md-3 mt16"> <div class="col-md-3 mt16">
@@ -692,7 +692,12 @@
<t t-foreach="quotations" t-as="quotation"> <t t-foreach="quotations" t-as="quotation">
<tr> <tr>
<td> <td>
<a t-attf-href="/quote/#{quotation.id}?#{keep_query()}"><t t-esc="quotation.name"/></a> <t t-if="quotation.sudo().partner_id.commercial_partner_id != request.env.user.partner_id.commercial_partner_id and request.env.user.share">
<a t-attf-href="/quote/#{quotation.id}/#{quotation.access_token}?#{keep_query()}"><t t-esc="quotation.name"/></a>
</t>
<t t-if="not (quotation.sudo().partner_id.commercial_partner_id != request.env.user.partner_id.commercial_partner_id and request.env.user.share)">
<a t-attf-href="/quote/#{quotation.id}?#{keep_query()}"><t t-esc="quotation.name"/></a>
</t>
</td> </td>
<td><span t-field="quotation.validity_date"/></td> <td><span t-field="quotation.validity_date"/></td>
<td> <td>
+2
View File
@@ -86,6 +86,8 @@ class BaseCase(unittest.TestCase):
initialized by subclasses. initialized by subclasses.
""" """
longMessage = True # more verbose error message by default: https://www.odoo.com/r/Vmh
def cursor(self): def cursor(self):
return self.registry.cursor() return self.registry.cursor()