mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2026-10-25 07:27:39 -05:00
Merge branch 'master-upstream-patch-25112021' into 'master'
Master upstream patch 25112021 See merge request flectra-hq/flectra!506
This commit is contained in:
commit
4e4593b880
16 files changed
+121
-18
No files matched your search
@@ -14,12 +14,13 @@ class Employee(models.Model):
|
||||
self.ensure_one()
|
||||
cars = self.env['fleet.vehicle.assignation.log'].search([
|
||||
('driver_id', 'in', (self.user_id.partner_id | self.sudo().address_home_id).ids)]).mapped('vehicle_id')
|
||||
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "fleet.vehicle",
|
||||
"views": [[False, "kanban"], [False, "form"], [False, "tree"]],
|
||||
"domain": [("id", "in", cars.ids)],
|
||||
"context": dict(create=False),
|
||||
"context": dict(self._context, create=False),
|
||||
"name": "History Employee Cars",
|
||||
}
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ class LinkTracker(models.Model):
|
||||
def action_view_statistics(self):
|
||||
action = self.env['ir.actions.act_window']._for_xml_id('link_tracker.link_tracker_click_action_statistics')
|
||||
action['domain'] = [('link_id', '=', self.id)]
|
||||
action['context'] = dict(action.get('context', {}), create=False)
|
||||
action['context'] = dict(self._context, create=False)
|
||||
return action
|
||||
|
||||
def action_visit_page(self):
|
||||
|
||||
@@ -112,6 +112,21 @@ class Message extends Component {
|
||||
* regular time.
|
||||
*/
|
||||
this._intervalId = undefined;
|
||||
/**
|
||||
* States the index of the last "read more" that was inserted.
|
||||
* Useful to remember the state for each "read more" even if their DOM
|
||||
* is re-rendered.
|
||||
*/
|
||||
this._lastReadMoreIndex = 0;
|
||||
/**
|
||||
* Determines whether each "read more" is opened or closed. The keys are
|
||||
* index, which is determined by their order of appearance in the DOM.
|
||||
* If body changes so that "read more" count is different, their default
|
||||
* value will be "wrong" at the next render but this is an acceptable
|
||||
* limitation. It's more important to save the state correctly in a
|
||||
* typical non-changing situation.
|
||||
*/
|
||||
this._isReadMoreByIndex = new Map();
|
||||
this._constructor();
|
||||
}
|
||||
|
||||
@@ -404,6 +419,7 @@ class Message extends Component {
|
||||
}
|
||||
|
||||
for (const group of groups) {
|
||||
const index = this._lastReadMoreIndex++;
|
||||
// Insert link just before the first node
|
||||
const $readMoreLess = $('<a>', {
|
||||
class: 'o_Message_readMoreLess',
|
||||
@@ -412,16 +428,23 @@ class Message extends Component {
|
||||
}).insertBefore(group[0]);
|
||||
|
||||
// Toggle All next nodes
|
||||
let isReadMore = true;
|
||||
$readMoreLess.click(e => {
|
||||
e.preventDefault();
|
||||
isReadMore = !isReadMore;
|
||||
if (!this._isReadMoreByIndex.has(index)) {
|
||||
this._isReadMoreByIndex.set(index, true);
|
||||
}
|
||||
const updateFromState = () => {
|
||||
const isReadMore = this._isReadMoreByIndex.get(index);
|
||||
for (const $child of group) {
|
||||
$child.hide();
|
||||
$child.toggle(!isReadMore);
|
||||
}
|
||||
$readMoreLess.text(isReadMore ? READ_MORE : READ_LESS);
|
||||
};
|
||||
$readMoreLess.click(e => {
|
||||
e.preventDefault();
|
||||
this._isReadMoreByIndex.set(index, !this._isReadMoreByIndex.get(index));
|
||||
updateFromState();
|
||||
});
|
||||
updateFromState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,6 +466,7 @@ class Message extends Component {
|
||||
for (const el of [...this._contentRef.el.querySelectorAll(':scope .o_Message_readMoreLess')]) {
|
||||
el.remove();
|
||||
}
|
||||
this._lastReadMoreIndex = 0;
|
||||
this._insertReadMoreLess($(this._contentRef.el));
|
||||
this.env.messagingBus.trigger('o-component-message-read-more-less-inserted', {
|
||||
message: this.message,
|
||||
|
||||
@@ -975,6 +975,81 @@ QUnit.test('chatter just contains "creating a new record" message during the cre
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('[TECHNICAL] unfolded read more/less links should not fold on message click besides those button links', async function (assert) {
|
||||
// message click triggers a re-render. Before writing of this test, the
|
||||
// insertion of read more/less links were done during render. This meant
|
||||
// any re-render would re-insert the read more/less links. If some button
|
||||
// links were unfolded, any re-render would fold them again.
|
||||
//
|
||||
// This previous behavior is undesirable, and results to bothersome UX
|
||||
// such as inability to copy/paste unfolded message content due to click
|
||||
// from text selection automatically folding all read more/less links.
|
||||
assert.expect(3);
|
||||
|
||||
this.data['mail.message'].records.push({
|
||||
author_id: 100,
|
||||
// "data-o-mail-quote" added by server is intended to be compacted in read more/less blocks
|
||||
body: `
|
||||
<div>
|
||||
Dear Joel Willis,<br>
|
||||
Thank you for your enquiry.<br>
|
||||
If you have any questions, please let us know.
|
||||
<br><br>
|
||||
Thank you,<br>
|
||||
<span data-o-mail-quote="1">-- <br data-o-mail-quote="1">
|
||||
System
|
||||
</span>
|
||||
</div>
|
||||
`,
|
||||
id: 1000,
|
||||
model: 'res.partner',
|
||||
res_id: 2,
|
||||
});
|
||||
this.data['res.partner'].records.push({
|
||||
display_name: "Someone",
|
||||
id: 100,
|
||||
});
|
||||
await this.createView({
|
||||
data: this.data,
|
||||
hasView: true,
|
||||
// View params
|
||||
View: FormView,
|
||||
model: 'res.partner',
|
||||
res_id: 2,
|
||||
arch: `
|
||||
<form string="Partners">
|
||||
<sheet>
|
||||
<field name="name"/>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids"/>
|
||||
</div>
|
||||
</form>
|
||||
`,
|
||||
});
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_Message_readMoreLess').textContent,
|
||||
"read more",
|
||||
"read more/less link on message should be folded initially (read more)"
|
||||
);
|
||||
|
||||
document.querySelector('.o_Message_readMoreLess').click(),
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_Message_readMoreLess').textContent,
|
||||
"read less",
|
||||
"read more/less link on message should be unfolded after a click from initial rendering (read less)"
|
||||
);
|
||||
|
||||
await afterNextRender(
|
||||
() => document.querySelector('.o_Message').click(),
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_Message_readMoreLess').textContent,
|
||||
"read less",
|
||||
"read more/less link on message should still be unfolded after a click on message aside of this button click (read less)"
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -393,7 +393,7 @@ class MassMailing(models.Model):
|
||||
'view_mode': 'tree',
|
||||
'res_model': self.mailing_model_real,
|
||||
'domain': [('id', 'in', res_ids)],
|
||||
'context': dict(create=False)
|
||||
'context': dict(self._context, create=False)
|
||||
}
|
||||
|
||||
def update_opt_out(self, email, list_ids, value):
|
||||
|
||||
@@ -407,7 +407,7 @@ class MrpProduction(models.Model):
|
||||
else:
|
||||
action['views'] = form_view
|
||||
action['res_id'] = pickings.id
|
||||
action['context'] = dict(action.get('context', {}), create=False, default_origin=self.name)
|
||||
action['context'] = dict(self._context, default_origin=self.name, create=False)
|
||||
return action
|
||||
|
||||
@api.depends('product_uom_id', 'product_qty', 'product_id.uom_id')
|
||||
@@ -1623,7 +1623,7 @@ class MrpProduction(models.Model):
|
||||
self.ensure_one()
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("stock.action_stock_scrap")
|
||||
action['domain'] = [('production_id', '=', self.id)]
|
||||
action['context'] = dict(action.get('context', {}), create=False)
|
||||
action['context'] = dict(self._context, default_origin=self.name)
|
||||
return action
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -1364,7 +1364,7 @@ class Task(models.Model):
|
||||
'res_model': 'project.task',
|
||||
'res_id': self.parent_id.id,
|
||||
'type': 'ir.actions.act_window',
|
||||
'context': dict(create=False)
|
||||
'context': dict(self._context, create=False)
|
||||
}
|
||||
|
||||
def action_subtask(self):
|
||||
|
||||
@@ -115,8 +115,9 @@ class PurchaseOrder(models.Model):
|
||||
|
||||
# Recompute prices for new/modified lines:
|
||||
for line in self.order_line.filtered(lambda line: line.product_id.id in product_ids):
|
||||
res = line._product_id_change() or res
|
||||
line._product_id_change()
|
||||
line._onchange_quantity()
|
||||
res = line.onchange_product_id_warning() or res
|
||||
return res
|
||||
|
||||
def _get_matrix(self, product_template):
|
||||
|
||||
@@ -283,5 +283,5 @@ class ProductionLot(models.Model):
|
||||
self.ensure_one()
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("purchase.purchase_form_action")
|
||||
action['domain'] = [('id', 'in', self.mapped('purchase_order_ids.id'))]
|
||||
action['context'] = dict(action.get('context', {}), create=False)
|
||||
action['context'] = dict(self._context, create=False)
|
||||
return action
|
||||
@@ -26,7 +26,7 @@ class CouponProgram(models.Model):
|
||||
'search_view_id': [self.env.ref('sale.sale_order_view_search_inherit_quotation').id],
|
||||
'type': 'ir.actions.act_window',
|
||||
'domain': [('id', 'in', orders.ids)],
|
||||
'context': dict(create=False),
|
||||
'context': dict(self._context, create=False),
|
||||
}
|
||||
|
||||
def _check_promo_code(self, order, coupon_code):
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<button type="object" name="action_view_task" class="oe_stat_button" icon="fa-tasks" attrs="{'invisible': [('tasks_count', '=', 0)]}" groups="project.group_project_user">
|
||||
<field name="tasks_count" widget="statinfo" string="Tasks"/>
|
||||
</button>
|
||||
<button type="object" name="action_view_project_ids" class="oe_stat_button" icon="fa-puzzle-piece" string="Project" attrs="{'invisible': [('project_ids', '=', [])]}" groups="project.group_project_manager">
|
||||
<button type="object" name="action_view_project_ids" class="oe_stat_button" icon="fa-puzzle-piece" string="Project" attrs="{'invisible': ['|', ('state', 'in', ['draft', 'sent']), ('project_ids', '=', [])]}" groups="project.group_project_manager">
|
||||
<field name="project_ids" invisible="1"/>
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
@@ -204,7 +204,7 @@ class SaleOrder(models.Model):
|
||||
picking_id = picking_id[0]
|
||||
else:
|
||||
picking_id = pickings[0]
|
||||
action['context'] = dict(action.get('context', {}), default_partner_id=self.partner_id.id, default_picking_type_id=picking_id.picking_type_id.id, default_origin=self.name, default_group_id=picking_id.group_id.id, group_by=[])
|
||||
action['context'] = dict(self._context, default_partner_id=self.partner_id.id, default_picking_type_id=picking_id.picking_type_id.id, default_origin=self.name, default_group_id=picking_id.group_id.id)
|
||||
return action
|
||||
|
||||
def action_cancel(self):
|
||||
|
||||
@@ -183,5 +183,5 @@ class ProductionLot(models.Model):
|
||||
self.ensure_one()
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("sale.action_orders")
|
||||
action['domain'] = [('id', 'in', self.mapped('sale_order_ids.id'))]
|
||||
action['context'] = dict(action.get('context', {}), create=False)
|
||||
action['context'] = dict(self._context, create=False)
|
||||
return action
|
||||
@@ -1375,7 +1375,7 @@ class Picking(models.Model):
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("stock.action_stock_scrap")
|
||||
scraps = self.env['stock.scrap'].search([('picking_id', '=', self.id)])
|
||||
action['domain'] = [('id', 'in', scraps.ids)]
|
||||
action['context'] = dict(action.get('context', {}), create=False)
|
||||
action['context'] = dict(self._context, create=False)
|
||||
return action
|
||||
|
||||
def action_see_packages(self):
|
||||
|
||||
@@ -28,7 +28,7 @@ class StockInventory(models.Model):
|
||||
self.ensure_one()
|
||||
action_data = self.env['ir.actions.act_window']._for_xml_id('account.action_move_journal_line')
|
||||
action_data['domain'] = [('stock_move_id.id', 'in', self.move_ids.ids)]
|
||||
action_data['context'] = dict(action_data.get('context', {}), create=False)
|
||||
action_data['context'] = dict(self._context, create=False)
|
||||
return action_data
|
||||
|
||||
def post_inventory(self):
|
||||
|
||||
@@ -196,6 +196,8 @@ class Page(models.Model):
|
||||
if not pages_linked_to_iruiview and not page.view_id.inherit_children_ids:
|
||||
# If there is no other pages linked to that ir_ui_view, we can delete the ir_ui_view
|
||||
page.view_id.unlink()
|
||||
# Make sure website._get_menu_ids() will be recomputed
|
||||
self.clear_caches()
|
||||
return super(Page, self).unlink()
|
||||
|
||||
def write(self, vals):
|
||||
|
||||
Reference in new issue
Block a user