Skip to content

Commit

Permalink
Merge remote-tracking branch 'odoo/18.0' into 18.0
Browse files Browse the repository at this point in the history
  • Loading branch information
OCA-git-bot committed Oct 29, 2024
2 parents 0f4486f + f4c7e3e commit 72f63cc
Show file tree
Hide file tree
Showing 85 changed files with 1,529 additions and 389 deletions.
3 changes: 2 additions & 1 deletion addons/calendar/models/calendar_recurrence.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,8 @@ def _rrule_parse(self, rule_str, date_start):
# Optional parameters starts with X- and they can be placed anywhere in the RRULE string.
# RRULE:FREQ=MONTHLY;INTERVAL=3;X-RELATIVE=1
# RRULE;X-EVOLUTION-ENDDATE=20200120:FREQ=WEEKLY;COUNT=3;BYDAY=MO
rule_str = re.sub(r';?X-[-\w]+=[^;:]*', '', rule_str).replace(":;", ":")
# X-EVOLUTION-ENDDATE=20200120:FREQ=WEEKLY;COUNT=3;BYDAY=MO
rule_str = re.sub(r';?X-[-\w]+=[^;:]*', '', rule_str).replace(":;", ":").lstrip(":;")

if 'Z' in rule_str and date_start and not date_start.tzinfo:
date_start = pytz.utc.localize(date_start)
Expand Down
7 changes: 7 additions & 0 deletions addons/calendar/tests/test_event_recurrence.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,13 @@ def test_rrule_x_params(self):
self.assertFalse(self.recurrence.tue)
self.assertTrue(self.recurrence.wed)

def test_rrule_x_params_no_rrule_prefix(self):
self.recurrence.rrule = 'X-EVOLUTION-ENDDATE=20371102T114500Z:FREQ=WEEKLY;COUNT=720;BYDAY=MO'
self.assertFalse(self.recurrence.tue)
self.assertTrue(self.recurrence.mon)
self.assertEqual(self.recurrence.count, 720)
self.assertEqual(self.recurrence.rrule_type, 'weekly')

def test_shift_all_base_inactive(self):
self.recurrence.base_event_id.active = False
event = self.events[1]
Expand Down
2 changes: 1 addition & 1 deletion addons/hr_expense/models/res_config_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ResConfigSettings(models.TransientModel):
config_parameter='hr_expense.use_mailgateway')
module_hr_payroll_expense = fields.Boolean(string='Reimburse Expenses in Payslip')
module_hr_expense_extract = fields.Boolean(string='Send bills to OCR to generate expenses')
expense_journal_id = fields.Many2one('account.journal', related='company_id.expense_journal_id', readonly=False, check_company=True)
expense_journal_id = fields.Many2one('account.journal', related='company_id.expense_journal_id', readonly=False, check_company=True, domain="[('type', '=', 'purchase')]")
expense_outstanding_account_id = fields.Many2one('account.account', related='company_id.expense_outstanding_account_id', readonly=False, check_company=True)
company_expense_allowed_payment_method_line_ids = fields.Many2many(
comodel_name='account.payment.method.line',
Expand Down
2 changes: 1 addition & 1 deletion addons/hr_holidays/models/hr_leave_allocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ def _get_future_leaves_on(self, accrual_date):
if self.type_request_unit in ['hour']:
return float_round(fake_allocation.number_of_hours_display - self.number_of_hours_display, precision_digits=2)
res = round((fake_allocation.number_of_days - self.number_of_days), 2)
self._invalidate_cache()
fake_allocation._invalidate_cache(['number_of_days', 'number_of_days_display', 'lastcall', 'nextcall', 'number_of_hours_display'])
return res

####################################################
Expand Down
57 changes: 49 additions & 8 deletions addons/hr_holidays/tests/test_accrual_allocations.py
Original file line number Diff line number Diff line change
Expand Up @@ -2170,6 +2170,48 @@ def test_check_lastcall_change_regular_to_accrual(self):

self.assertEqual(allocation.lastcall, datetime.date(2017, 12, 5))

def test_accrual_allocation_data_persists(self):
leave_type = self.env['hr.leave.type'].create({
'name': 'Test Leave Type',
'time_type': 'leave',
'requires_allocation': 'yes',
'allocation_validation_type': 'no_validation',
})
accrual_plan = self.env['hr.leave.accrual.plan'].create({
'name': 'Accrual Plan For Test',
'accrued_gain_time': 'start',
'carryover_date': 'year_start',
'level_ids': [(0, 0, {
'start_count': 1,
'start_type': 'day',
'added_value': 1,
'added_value_type': 'day',
'frequency': 'daily',
'cap_accrued_time': True,
'maximum_leave': 10
})],
})

def get_remaining_leaves(*args):
return leave_type.get_allocation_data(self.employee_emp, datetime.date(*args))[self.employee_emp][0][1][
'remaining_leaves']

with freeze_time("2024-03-01"):
# Simulate creating an allocation from frontend interface
with Form(self.env['hr.leave.allocation']) as f:
f.allocation_type = "accrual"
f.accrual_plan_id = accrual_plan
f.employee_id = self.employee_emp
f.holiday_status_id = leave_type
f.date_from = '2024-02-01'
f.name = "Accrual allocation for employee"

allocation = f.record
allocation.action_validate()

first_result = get_remaining_leaves(2024, 2, 21)
self.assertEqual(get_remaining_leaves(2024, 2, 21), first_result, "Function return result should persist")

def test_future_accural_time_with_leaves_taken_in_the_past(self):
leave_type = self.env['hr.leave.type'].create({
'name': 'Test Leave Type',
Expand All @@ -2192,7 +2234,7 @@ def test_future_accural_time_with_leaves_taken_in_the_past(self):
})],
})

def get_reamining_leaves(*args):
def get_remaining_leaves(*args):
return leave_type.get_allocation_data(self.employee_emp, datetime.date(*args))[self.employee_emp][0][1][
'remaining_leaves']

Expand All @@ -2208,7 +2250,7 @@ def get_reamining_leaves(*args):

allocation = f.record
allocation.action_validate()
self.assertEqual(get_reamining_leaves(2024, 3, 1), 10, "The cap is reached, no more leaves should be accrued")
self.assertEqual(get_remaining_leaves(2024, 3, 1), 10, "The cap is reached, no more leaves should be accrued")

leave = self.env['hr.leave'].create({
'name': 'leave',
Expand All @@ -2218,10 +2260,9 @@ def get_reamining_leaves(*args):
'request_date_to': '2024-03-01',
})
leave.action_validate()
self.assertEqual(get_reamining_leaves(2024, 3, 1), 5, "5 day should be deduced from the allocation")
self.assertEqual(get_reamining_leaves(2024, 3, 3), 7, "2 days should be added to the accrual allocation")
self.assertEqual(get_reamining_leaves(2024, 3, 3), 7, "Function return result should persist")
self.assertEqual(get_reamining_leaves(2024, 3, 10), 10, "Accrual allocation should be capped at 10")
self.assertEqual(get_remaining_leaves(2024, 3, 1), 5, "5 day should be deduced from the allocation")
self.assertEqual(get_remaining_leaves(2024, 3, 3), 7, "2 days should be added to the accrual allocation")
self.assertEqual(get_remaining_leaves(2024, 3, 10), 10, "Accrual allocation should be capped at 10")

leave = self.env['hr.leave'].create({
'name': 'leave',
Expand All @@ -2231,8 +2272,8 @@ def get_reamining_leaves(*args):
'request_date_to': '2024-03-08',
})
leave.action_validate()
self.assertEqual(get_reamining_leaves(2024, 3, 4), 3, "5 days should be deduced from the allocation and a new day should be accrued")
self.assertEqual(get_reamining_leaves(2024, 3, 11), 10, "Accrual allocation should be capped at 10")
self.assertEqual(get_remaining_leaves(2024, 3, 4), 3, "5 days should be deduced from the allocation and a new day should be accrued")
self.assertEqual(get_remaining_leaves(2024, 3, 11), 10, "Accrual allocation should be capped at 10")

@freeze_time('2024-01-01')
def test_validate_leaves_with_more_days_than_allocation(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
</button>
<t t-set-slot="content">
<t t-foreach="state.languages" t-as="language" t-key="language[0]">
<DropdownItem onSelected="() => this.onSelected(language[1])">
<div class="user-select-none lang" t-esc="language[1]"/>
<DropdownItem class="'user-select-none'" onSelected="() => this.onSelected(language[1])">
<div class="lang" t-esc="language[1]"/>
</DropdownItem>
</t>
</t>
Expand Down
4 changes: 2 additions & 2 deletions addons/html_editor/static/src/main/font/font_selector.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
</button>
<t t-set-slot="content">
<t t-foreach="items" t-as="item" t-key="item_index">
<DropdownItem onSelected="() => this.onSelected(item)">
<div class="user-select-none" t-esc="item.name"/>
<DropdownItem class="'user-select-none'" onSelected="() => this.onSelected(item)">
<t t-esc="item.name"/>
</DropdownItem>
</t>
</t>
Expand Down
4 changes: 2 additions & 2 deletions addons/html_editor/static/src/main/media/image_padding.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<button t-att-title="props.title" class="btn btn-light fa fa-plus-square-o"> </button>
<t t-set-slot="content">
<t t-foreach="this.paddings" t-as="padding" t-key="padding">
<DropdownItem onSelected="() => this.onSelected(padding)">
<div class="user-select-none" t-esc="padding"/>
<DropdownItem class="'user-select-none'" onSelected="() => this.onSelected(padding)">
<t t-esc="padding"/>
</DropdownItem>
</t>
</t>
Expand Down
4 changes: 3 additions & 1 deletion addons/html_editor/static/src/main/toolbar/mobile_toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export class ToolbarMobile extends Component {
setup() {
this.toolbar = useRef("toolbarWrapper");
useExternalListener(window.visualViewport, "resize", this.fixToolbarPosition);
useExternalListener(window.visualViewport, "scroll", this.fixToolbarPosition);
onMounted(() => {
this.fixToolbarPosition();
});
Expand All @@ -20,7 +21,8 @@ export class ToolbarMobile extends Component {
* Fixes the position of the toolbar for the keyboard height.
*/
fixToolbarPosition() {
const keyboardHeight = window.innerHeight - window.visualViewport.height;
const keyboardHeight =
window.innerHeight - (window.visualViewport.height + window.visualViewport.offsetTop);
if (keyboardHeight > 0) {
this.toolbar.el.style.bottom = `${keyboardHeight}px`;
} else {
Expand Down
1 change: 1 addition & 0 deletions addons/html_editor/static/src/utils/formatting.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const FONT_SIZE_CLASSES = [
"h6-fs",
"base-fs",
"small",
"o_small-fs",
];

export const TEXT_STYLE_CLASSES = ["display-1", "display-2", "display-3", "display-4", "lead"];
Expand Down
12 changes: 6 additions & 6 deletions addons/html_editor/static/tests/image.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,34 +205,34 @@ test("Can change the padding of an image", async () => {

await click(".o-we-toolbar div[name='image_padding'] button");
await animationFrame();
await click(".o_popover div:contains('Small')");
await click(".o_popover span:contains('Small')");
await animationFrame();
expect("img").toHaveClass("p-1");

await click(".o-we-toolbar div[name='image_padding'] button");
await animationFrame();
await click(".o_popover div:contains('Medium')");
await click(".o_popover span:contains('Medium')");
await animationFrame();
expect("img").not.toHaveClass("p-1");
expect("img").toHaveClass("p-2");

await click(".o-we-toolbar div[name='image_padding'] button");
await animationFrame();
await click(".o_popover div:contains('Large')");
await click(".o_popover span:contains('Large')");
await animationFrame();
expect("img").not.toHaveClass("p-2");
expect("img").toHaveClass("p-3");

await click(".o-we-toolbar div[name='image_padding'] button");
await animationFrame();
await click(".o_popover div:contains('XL')");
await click(".o_popover span:contains('XL')");
await animationFrame();
expect("img").not.toHaveClass("p-3");
expect("img").toHaveClass("p-5");

await click(".o-we-toolbar div[name='image_padding'] button");
await animationFrame();
await click(".o_popover div:contains('None')");
await click(".o_popover span:contains('None')");
await animationFrame();
expect("img").not.toHaveClass("p-5");
});
Expand All @@ -246,7 +246,7 @@ test("Can undo the image padding", async () => {

await click(".o-we-toolbar div[name='image_padding'] button");
await animationFrame();
await click(".o_popover div:contains('Small')");
await click(".o_popover span:contains('Small')");
await animationFrame();
expect("img").toHaveClass("p-1");

Expand Down
5 changes: 5 additions & 0 deletions addons/html_editor/static/tests/toolbar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,11 @@ test("toolbar works: can select font size", async () => {
await contains(`.o_font_selector_menu .dropdown-item:contains('${h1Size}')`).click();
expect(getContent(el)).toBe(`<p><span class="h1-fs">[test]</span></p>`);
expect(".o-we-toolbar [name='font-size']").toHaveText(h1Size);
await contains(".o-we-toolbar [name='font-size'] .dropdown-toggle").click();
const oSmallSize = getFontSizeFromVar("small-font-size").toString();
await contains(`.o_font_selector_menu .dropdown-item:contains('${oSmallSize}')`).click();
expect(getContent(el)).toBe(`<p><span class="o_small-fs">[test]</span></p>`);
expect(".o-we-toolbar [name='font-size']").toHaveText(oSmallSize);
});

test.tags("desktop")("toolbar should not open on keypress tab inside table", async () => {
Expand Down
2 changes: 1 addition & 1 deletion addons/l10n_ug/data/template/account.account-ug.csv
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ id,name,code,account_type,tag_ids,reconcile
352704,Swaps,352704,asset_prepayments,,False
3528,Account Receivable,3528,asset_receivable,,True
352802,Staff Advances,352802,asset_receivable,,True
352804,Taxes Receivable,352804,asset_receivable,,True
352804,Taxes Receivable,352804,asset_current,,True
352805,Revenue receivable,352805,asset_receivable,,True
352806,Trade debtors,352806,asset_receivable,,True
352807,Sundry Debtors,352807,asset_receivable,,True
Expand Down
1 change: 1 addition & 0 deletions addons/mail/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
from . import ir_action_act_window
from . import ir_actions_server
from . import ir_attachment
from . import ir_binary
from . import ir_config_parameter
from . import ir_cron
from . import ir_http
Expand Down
18 changes: 18 additions & 0 deletions addons/mail/models/ir_binary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import models


class IrBinary(models.AbstractModel):
_inherit = ["ir.binary"]

def _find_record_check_access(self, record, access_token, field):
if record._name in ["res.partner", "mail.guest"] and field == "avatar_128":
# access to the avatar is allowed if there is access to the messages
if record._name == "res.partner":
domain = [("author_id", "=", record.id)]
else:
domain = [("author_guest_id", "=", record.id)]
if self.env["mail.message"].search_count(domain, limit=1):
return record.sudo()
return super()._find_record_check_access(record, access_token, field)
4 changes: 2 additions & 2 deletions addons/mail/models/mail_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -1545,8 +1545,8 @@ def _message_parse_extract_payload(self, message: EmailMessage, message_dict: di
continue # skip container

filename = part.get_filename() # I may not properly handle all charsets
if part.get_content_type() == 'text/xml' and not part.get_param('charset'):
# for text/xml with omitted charset, the charset is assumed to be ASCII by the `email` module
if part.get_content_type().startswith('text/') and not part.get_param('charset'):
# for text/* with omitted charset, the charset is assumed to be ASCII by the `email` module
# although the payload might be in UTF8
part.set_charset('utf-8')
encoding = part.get_content_charset() # None if attachment
Expand Down
1 change: 1 addition & 0 deletions addons/mail/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from . import test_attachment_controller
from . import test_binary_controller
from . import test_controller_common
from . import test_discuss_tools
from . import test_ir_mail_server
Expand Down
1 change: 1 addition & 0 deletions addons/mail/tests/discuss/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from . import test_avatar_acl
from . import test_bus_presence
from . import test_discuss_attachment_controller
from . import test_discuss_binary_controller
from . import test_discuss_channel
from . import test_discuss_channel_access
from . import test_discuss_channel_as_guest
Expand Down
Loading

0 comments on commit 72f63cc

Please sign in to comment.