mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2026-10-25 07:27:39 -05:00
[PATCH] Upstream patch - 20042023
This commit is contained in:
1 parent
f4b35fc120
commit
2d35ca8d13
25 files changed
+363
-39
No files matched your search
@@ -749,8 +749,9 @@ class AccountMove(models.Model):
|
||||
self.company_id,
|
||||
self.date or fields.Date.context_today(self),
|
||||
)
|
||||
amount_currency = currency.round(taxes_map_entry['amount'])
|
||||
to_write_on_line = {
|
||||
'amount_currency': taxes_map_entry['amount'],
|
||||
'amount_currency': amount_currency,
|
||||
'currency_id': taxes_map_entry['grouping_dict']['currency_id'],
|
||||
'debit': balance > 0.0 and balance or 0.0,
|
||||
'credit': balance < 0.0 and -balance or 0.0,
|
||||
|
||||
@@ -301,7 +301,11 @@ class AccountTax(models.Model):
|
||||
|
||||
# We first need to find out whether this tax computation is made for a refund
|
||||
tax_type = self and self[0].type_tax_use
|
||||
is_refund = is_refund or (tax_type == 'sale' and price_unit > 0) or (tax_type == 'purchase' and price_unit < 0)
|
||||
|
||||
if self._context.get('manual_reco_widget'):
|
||||
is_refund = is_refund or (tax_type == 'sale' and price_unit > 0) or (tax_type == 'purchase' and price_unit < 0)
|
||||
else:
|
||||
is_refund = is_refund or (tax_type == 'sale' and price_unit < 0) or (tax_type == 'purchase' and price_unit > 0)
|
||||
|
||||
rslt = self.with_context(caba_no_transition_account=True)\
|
||||
.compute_all(price_unit, currency=currency_id, quantity=quantity, product=product_id, partner=partner_id, is_refund=is_refund)
|
||||
|
||||
@@ -1620,6 +1620,43 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
|
||||
'amount_total': 1409.95,
|
||||
})
|
||||
|
||||
def test_cash_rounding_with_rounding_method_set_to_global(self):
|
||||
"""
|
||||
Test that cash roundings are computed correctly when
|
||||
tax calculation rounding method is set to `round_globally`.
|
||||
"""
|
||||
self.env.company.tax_calculation_rounding_method = 'round_globally'
|
||||
|
||||
cash_rounding = self.env['account.cash.rounding'].create({
|
||||
'name': 'add_invoice_line',
|
||||
'rounding': 0.05,
|
||||
'strategy': 'add_invoice_line',
|
||||
'profit_account_id': self.company_data['default_account_revenue'].copy().id,
|
||||
'loss_account_id': self.company_data['default_account_expense'].copy().id,
|
||||
'rounding_method': 'HALF-UP',
|
||||
})
|
||||
|
||||
tax7_7 = self.env['account.tax'].create({
|
||||
'name': "tax7_7",
|
||||
'amount_type': 'percent',
|
||||
'amount': 7.7,
|
||||
})
|
||||
|
||||
invoice = self.env['account.move'].create({
|
||||
'move_type': 'out_invoice',
|
||||
'invoice_date': '2019-01-01',
|
||||
'partner_id': self.partner_a.id,
|
||||
'invoice_cash_rounding_id': cash_rounding.id,
|
||||
'invoice_line_ids': [
|
||||
(0, 0, {
|
||||
'name': 'test product',
|
||||
'price_unit': 295.0,
|
||||
'tax_ids': [(6, 0, tax7_7.ids)],
|
||||
}),
|
||||
],
|
||||
})
|
||||
self.assertEqual(invoice.amount_total, 317.70)
|
||||
|
||||
def test_out_invoice_line_onchange_currency_1(self):
|
||||
move_form = Form(self.invoice.with_context(dudu=True))
|
||||
move_form.currency_id = self.currency_data['currency']
|
||||
|
||||
@@ -1221,7 +1221,7 @@ class Message(models.Model):
|
||||
for record in self:
|
||||
model = model or record.model
|
||||
res_id = res_id or record.res_id
|
||||
if issubclass(self.pool[model], self.pool['mail.thread']):
|
||||
if model and issubclass(self.pool[model], self.pool['mail.thread']):
|
||||
self.env[model].invalidate_cache(fnames=[
|
||||
'message_ids',
|
||||
'message_unread',
|
||||
|
||||
@@ -193,6 +193,7 @@ function factory(dependencies) {
|
||||
this.env.bus.trigger('do-action', {
|
||||
action: 'mail.action_discuss',
|
||||
options: {
|
||||
name: this.env._t("Discuss"),
|
||||
active_id: this.threadToActiveId(this),
|
||||
clear_breadcrumbs: false,
|
||||
on_reverse_breadcrumb: () => this.close(),
|
||||
|
||||
@@ -128,6 +128,9 @@ class MassMailController(http.Controller):
|
||||
'class="o_snippet_view_in_browser" style="display: none;"'
|
||||
)
|
||||
|
||||
if res_id:
|
||||
res[mailing_id] = mailing._render_template(res[mailing_id], mailing.mailing_model_real, [res_id], post_process=True)[res_id]
|
||||
|
||||
return request.render('mass_mailing.view', {
|
||||
'body': res[mailing_id],
|
||||
})
|
||||
|
||||
@@ -20,3 +20,22 @@ class TestMassMailingControllers(MassMailCommon, HttpCase):
|
||||
|
||||
response = self.url_open(url)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_mailing_view(self):
|
||||
mailing = self.env['mailing.mailing'].create({
|
||||
'name': 'TestMailing',
|
||||
'subject': 'Test',
|
||||
'mailing_type': 'mail',
|
||||
'body_html': '<p>Hello ${object.name} ${object.hello}</p>',
|
||||
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
||||
})
|
||||
|
||||
partner_id = self.user_admin.partner_id
|
||||
self.authenticate('admin', 'admin')
|
||||
|
||||
url = werkzeug.urls.url_join(mailing.get_base_url(), '/mailing/%s/view?res_id=%s' % (mailing.id, partner_id.id))
|
||||
response = self.url_open(url)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotIn('${', response.text)
|
||||
self.assertIn("<p>Hello %s </p>" % partner_id.name, response.text)
|
||||
@@ -259,7 +259,7 @@ class TestMrpAccountMove(TestAccountMove):
|
||||
"name": "Product B",
|
||||
"type": "product",
|
||||
"default_code": "prda",
|
||||
"categ_id": cls.env.ref("product.product_category_all").id,
|
||||
"categ_id": cls.auto_categ.id,
|
||||
"taxes_id": [(5, 0, 0)],
|
||||
"supplier_taxes_id": [(5, 0, 0)],
|
||||
"lst_price": 100.0,
|
||||
|
||||
@@ -189,7 +189,8 @@ class PosOrder(models.Model):
|
||||
order.add_payment(return_payment_vals)
|
||||
|
||||
def _prepare_invoice_line(self, order_line):
|
||||
name = order_line.product_id.default_code + " " + order_line.product_id.display_name if order_line.product_id.default_code else order_line.product_id.display_name
|
||||
display_name = order_line.product_id.get_product_multiline_description_sale()
|
||||
name = order_line.product_id.default_code + " " + display_name if order_line.product_id.default_code else display_name
|
||||
return {
|
||||
'product_id': order_line.product_id.id,
|
||||
'quantity': order_line.qty if self.amount_total >= 0 else -order_line.qty,
|
||||
|
||||
@@ -62,7 +62,7 @@ class PurchaseReport(models.Model):
|
||||
@property
|
||||
def _table_query(self):
|
||||
''' Report needs to be dynamic to take into account multi-company selected + multi-currency rates '''
|
||||
return '%s %s %s' % (self._select(), self._from(), self._group_by())
|
||||
return '%s %s %s %s' % (self._select(), self._from(), self._where(), self._group_by())
|
||||
|
||||
def _select(self):
|
||||
select_str = """
|
||||
@@ -120,6 +120,12 @@ class PurchaseReport(models.Model):
|
||||
)
|
||||
return from_str
|
||||
|
||||
def _where(self):
|
||||
return """
|
||||
WHERE
|
||||
l.display_type IS NULL
|
||||
"""
|
||||
|
||||
def _group_by(self):
|
||||
group_by_str = """
|
||||
GROUP BY
|
||||
|
||||
@@ -89,6 +89,36 @@ class TestPurchaseOrderReport(AccountTestInvoicingCommon):
|
||||
self.assertEqual(round(report[0]['delay']), -10, msg="The PO has been confirmed 10 days in advance")
|
||||
self.assertEqual(round(report[0]['delay_pass']), 5, msg="There are 5 days between the order date and the planned date")
|
||||
|
||||
def test_02_po_report_note_section_filter(self):
|
||||
po = self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'currency_id': self.currency_data['currency'].id,
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'name': 'This is a note',
|
||||
'display_type': 'line_note',
|
||||
'product_id': False,
|
||||
'product_qty': 0.0,
|
||||
'product_uom': False,
|
||||
'price_unit': 0.0,
|
||||
'taxes_id': False,
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': 'This is a section',
|
||||
'display_type': 'line_section',
|
||||
'product_id': False,
|
||||
'product_qty': 0.0,
|
||||
'product_uom': False,
|
||||
'price_unit': 0.0,
|
||||
'taxes_id': False,
|
||||
}),
|
||||
],
|
||||
})
|
||||
po.button_confirm()
|
||||
|
||||
result_po = self.env['purchase.report'].search([('order_id', '=', po.id)])
|
||||
self.assertFalse(result_po, "The report should ignore the notes and sections")
|
||||
|
||||
def test_avg_price_calculation(self):
|
||||
"""
|
||||
Check that the average price is calculated based on the quantity ordered in each line
|
||||
|
||||
@@ -20,6 +20,7 @@ def _configure_journals(cr, registry):
|
||||
'property_stock_valuation_account_id',
|
||||
]
|
||||
# Property Stock Accounts
|
||||
categ_values = {category.id: False for category in env['product.category'].search([])}
|
||||
for company_id in company_ids:
|
||||
# Check if property exists for stock account journal exists
|
||||
field = env['ir.model.fields']._get("product.category", "property_stock_journal")
|
||||
@@ -27,10 +28,10 @@ def _configure_journals(cr, registry):
|
||||
('fields_id', '=', field.id),
|
||||
('company_id', '=', company_id.id)])
|
||||
|
||||
# If not, check if you can find a journal that is already there with the same name, otherwise create one
|
||||
# If not, check if you can find a journal that is already there with the same code, otherwise create one
|
||||
if not properties:
|
||||
journal_id = env['account.journal'].search([
|
||||
('name', '=', _('Inventory Valuation')),
|
||||
('code', '=', 'STJ'),
|
||||
('company_id', '=', company_id.id),
|
||||
('type', '=', 'general')], limit=1).id
|
||||
if not journal_id:
|
||||
@@ -57,10 +58,4 @@ def _configure_journals(cr, registry):
|
||||
account,
|
||||
company_id,
|
||||
)
|
||||
for name in todo_list:
|
||||
env['ir.property']._set_multi(
|
||||
name,
|
||||
'product.category',
|
||||
{category.id: False for category in env['product.category'].search([])},
|
||||
True
|
||||
)
|
||||
env['ir.property'].with_company(company_id.id)._set_multi(name, 'product.category', categ_values, True)
|
||||
@@ -27,9 +27,11 @@ class AccountChartTemplate(models.Model):
|
||||
'property_stock_account_output_categ_id',
|
||||
'property_stock_valuation_account_id',
|
||||
]
|
||||
categ_values = {category.id: False for category in self.env['product.category'].search([])}
|
||||
for field in todo_list:
|
||||
account = self[field]
|
||||
value = acc_template_ref[account.id] if account else False
|
||||
PropertyObj._set_default(field, "product.category", value, company)
|
||||
PropertyObj._set_multi(field, "product.category", categ_values, True)
|
||||
|
||||
return res
|
||||
@@ -222,7 +222,7 @@ class AccountMoveLine(models.Model):
|
||||
# with anglo-saxon accounting.
|
||||
self.ensure_one()
|
||||
self = self.with_company(self.move_id.journal_id.company_id)
|
||||
if self.product_id.type == 'product' \
|
||||
if self._can_use_stock_accounts() \
|
||||
and self.move_id.company_id.anglo_saxon_accounting \
|
||||
and self.move_id.is_purchase_document():
|
||||
fiscal_position = self.move_id.fiscal_position_id
|
||||
@@ -231,6 +231,9 @@ class AccountMoveLine(models.Model):
|
||||
return accounts['stock_input']
|
||||
return super(AccountMoveLine, self)._get_computed_account()
|
||||
|
||||
def _can_use_stock_accounts(self):
|
||||
return self.product_id.type == 'product'
|
||||
|
||||
def _stock_account_get_anglo_saxon_price_unit(self):
|
||||
self.ensure_one()
|
||||
if not self.product_id:
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from flectra.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from flectra.addons.stock_account.tests.test_stockvaluation import _create_accounting_data
|
||||
from flectra.tests.common import tagged, Form
|
||||
from flectra import fields
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
@@ -20,12 +21,25 @@ class TestAccountMove(AccountTestInvoicingCommon):
|
||||
cls.stock_journal,
|
||||
) = _create_accounting_data(cls.env)
|
||||
|
||||
# `all_categ` should not be altered, so we can test the `post_init` hook of `stock_account`
|
||||
cls.all_categ = cls.env.ref('product.product_category_all')
|
||||
|
||||
cls.auto_categ = cls.env['product.category'].create({
|
||||
'name': 'child_category',
|
||||
'parent_id': cls.all_categ.id,
|
||||
"property_stock_account_input_categ_id": cls.stock_input_account.id,
|
||||
"property_stock_account_output_categ_id": cls.stock_output_account.id,
|
||||
"property_stock_valuation_account_id": cls.stock_valuation_account.id,
|
||||
"property_stock_journal": cls.stock_journal.id,
|
||||
"property_valuation": "real_time",
|
||||
"property_cost_method": "standard",
|
||||
})
|
||||
cls.product_A = cls.env["product.product"].create(
|
||||
{
|
||||
"name": "Product A",
|
||||
"type": "product",
|
||||
"default_code": "prda",
|
||||
"categ_id": cls.env.ref("product.product_category_all").id,
|
||||
"categ_id": cls.auto_categ.id,
|
||||
"taxes_id": [(5, 0, 0)],
|
||||
"supplier_taxes_id": [(5, 0, 0)],
|
||||
"lst_price": 100.0,
|
||||
@@ -34,16 +48,6 @@ class TestAccountMove(AccountTestInvoicingCommon):
|
||||
"property_account_expense_id": cls.company_data["default_account_expense"].id,
|
||||
}
|
||||
)
|
||||
cls.product_A.categ_id.write(
|
||||
{
|
||||
"property_stock_account_input_categ_id": cls.stock_input_account.id,
|
||||
"property_stock_account_output_categ_id": cls.stock_output_account.id,
|
||||
"property_stock_valuation_account_id": cls.stock_valuation_account.id,
|
||||
"property_stock_journal": cls.stock_journal.id,
|
||||
"property_valuation": "real_time",
|
||||
"property_cost_method": "standard",
|
||||
}
|
||||
)
|
||||
|
||||
def test_standard_perpetual_01_mc_01(self):
|
||||
rate = self.currency_data["rates"].sorted()[0].rate
|
||||
@@ -118,3 +122,33 @@ class TestAccountMove(AccountTestInvoicingCommon):
|
||||
self.assertEqual(len(invoice.mapped("line_ids")), 4)
|
||||
self.assertEqual(len(invoice.mapped("line_ids").filtered("is_anglo_saxon_line")), 2)
|
||||
self.assertEqual(len(invoice.mapped("line_ids.currency_id")), 2)
|
||||
|
||||
def test_basic_bill(self):
|
||||
"""
|
||||
When billing a storable product with a basic category (manual
|
||||
valuation), the account used should be the expenses one. This test
|
||||
checks the flow with two companies:
|
||||
- One that existed before the installation of `stock_account` (to test
|
||||
the post-install hook)
|
||||
- One created after the module installation
|
||||
"""
|
||||
first_company = self.env['res.company'].browse(1)
|
||||
self.env.user.company_ids |= first_company
|
||||
basic_product = self.env['product.product'].create({
|
||||
'name': 'SuperProduct',
|
||||
'type': 'product',
|
||||
'categ_id': self.all_categ.id,
|
||||
})
|
||||
|
||||
for company in (self.env.company | first_company):
|
||||
bill_form = Form(self.env['account.move'].with_company(company.id).with_context(default_move_type='in_invoice'))
|
||||
bill_form.partner_id = self.partner_a
|
||||
bill_form.invoice_date = fields.Date.today()
|
||||
with bill_form.invoice_line_ids.new() as line:
|
||||
line.product_id = basic_product
|
||||
line.price_unit = 100
|
||||
bill = bill_form.save()
|
||||
bill.action_post()
|
||||
|
||||
product_accounts = basic_product.product_tmpl_id.with_company(company.id).get_product_accounts()
|
||||
self.assertEqual(bill.invoice_line_ids.account_id, product_accounts['expense'])
|
||||
@@ -53,9 +53,6 @@ class AccountMoveLine(models.Model):
|
||||
product_type = fields.Selection(related='product_id.type', readonly=True)
|
||||
is_landed_costs_line = fields.Boolean()
|
||||
|
||||
def _get_landed_costs_account(self, accounts):
|
||||
return accounts['stock_input' if self.move_id.company_id.anglo_saxon_accounting else 'expense']
|
||||
|
||||
@api.onchange('is_landed_costs_line')
|
||||
def _onchange_is_landed_costs_line(self):
|
||||
"""Mark an invoice line as a landed cost line and adapt `self.account_id`. The default
|
||||
@@ -66,7 +63,7 @@ class AccountMoveLine(models.Model):
|
||||
if self.product_type != 'service':
|
||||
self.is_landed_costs_line = False
|
||||
elif self.is_landed_costs_line:
|
||||
aml_account = self._get_landed_costs_account(accounts)
|
||||
aml_account = (self.move_id.company_id.anglo_saxon_accounting and accounts['stock_input']) or aml_account
|
||||
self.account_id = aml_account
|
||||
|
||||
@api.onchange('product_id')
|
||||
@@ -76,8 +73,5 @@ class AccountMoveLine(models.Model):
|
||||
else:
|
||||
self.is_landed_costs_line = False
|
||||
|
||||
def create(self, vals_list):
|
||||
lines = super().create(vals_list)
|
||||
for line in lines.filtered(lambda x: x.is_landed_costs_line and x.product_id.categ_id.property_valuation == 'real_time'):
|
||||
line.account_id = line._get_landed_costs_account(line.product_id.product_tmpl_id._get_product_accounts())
|
||||
return lines
|
||||
def _can_use_stock_accounts(self):
|
||||
return super()._can_use_stock_accounts() or (self.product_id.type == 'service' and self.product_id.landed_cost_ok)
|
||||
@@ -4,6 +4,7 @@
|
||||
from flectra.addons.stock_landed_costs.tests.common import TestStockLandedCostsCommon
|
||||
from flectra.exceptions import ValidationError
|
||||
from flectra.tests import tagged
|
||||
from flectra import fields
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
@@ -159,3 +160,53 @@ class TestStockLandedCosts(TestStockLandedCostsCommon):
|
||||
|
||||
self.assertEqual(len(picking_landed_cost_1.move_lines.stock_valuation_layer_ids), 5)
|
||||
self.assertEqual(len(picking_landed_cost_2.move_lines.stock_valuation_layer_ids), 5)
|
||||
|
||||
def test_aml_account_selection(self):
|
||||
"""
|
||||
Process a PO with a landed cost, then create and post the bill. The
|
||||
account of the landed cost AML should be:
|
||||
- Expense if the categ valuation is manual
|
||||
- Stock IN if the categ valuation is real time
|
||||
"""
|
||||
self.landed_cost.landed_cost_ok = True
|
||||
|
||||
for valuation, account in [
|
||||
('manual_periodic', self.company_data['default_account_expense']),
|
||||
('real_time', self.env.company.property_stock_account_input_categ_id),
|
||||
]:
|
||||
self.landed_cost.categ_id.property_valuation = valuation
|
||||
po = self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'currency_id': self.company_data['currency'].id,
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'name': self.product_a.name,
|
||||
'product_id': self.product_a.id,
|
||||
'product_qty': 1.0,
|
||||
'product_uom': self.product_a.uom_po_id.id,
|
||||
'price_unit': 100.0,
|
||||
'taxes_id': False,
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': self.landed_cost.name,
|
||||
'product_id': self.landed_cost.id,
|
||||
'product_qty': 1.0,
|
||||
'price_unit': 100.0,
|
||||
}),
|
||||
],
|
||||
})
|
||||
po.button_confirm()
|
||||
|
||||
receipt = po.picking_ids
|
||||
receipt.move_lines.quantity_done = 1
|
||||
receipt.button_validate()
|
||||
po.order_line[1].qty_received = 1
|
||||
|
||||
po.action_create_invoice()
|
||||
bill = po.invoice_ids
|
||||
bill.invoice_date = fields.Date.today()
|
||||
bill._post()
|
||||
|
||||
landed_cost_aml = bill.invoice_line_ids.filtered(lambda l: l.product_id == self.landed_cost)
|
||||
self.assertEqual(bill.state, 'posted', 'Incorrect value with valuation %s' % valuation)
|
||||
self.assertEqual(landed_cost_aml.account_id, account, 'Incorrect value with valuation %s' % valuation)
|
||||
@@ -69,6 +69,8 @@ class TestLandedCosts(TestStockLandedCostsCommon):
|
||||
# 3.transportation 250 By Weight
|
||||
# 4.packaging 20 By Volume
|
||||
|
||||
self.landed_cost.categ_id.property_valuation = 'real_time'
|
||||
|
||||
# Process incoming shipment
|
||||
income_ship = self._process_incoming_shipment()
|
||||
# Create landed costs
|
||||
@@ -181,6 +183,8 @@ class TestLandedCosts(TestStockLandedCostsCommon):
|
||||
# 3.transportation -50 By Weight
|
||||
# 4.packaging -5 By Volume
|
||||
|
||||
self.landed_cost.categ_id.property_valuation = 'real_time'
|
||||
|
||||
# Process incoming shipment
|
||||
income_ship = self._process_incoming_shipment()
|
||||
# Refrigerator outgoing shipment.
|
||||
|
||||
@@ -1164,6 +1164,9 @@ var SnippetsMenu = Widget.extend({
|
||||
}
|
||||
const $oeStructure = $target.closest('.oe_structure');
|
||||
if ($oeStructure.length && !$oeStructure.children().length && this.$snippets) {
|
||||
// Deselect any snippet that might be selected (e.g. within a
|
||||
// mega menu)
|
||||
this._activateSnippet(false);
|
||||
// If empty oe_structure, encourage using snippets in there by
|
||||
// making them "wizz" in the panel.
|
||||
this.$snippets.flectraBounce();
|
||||
|
||||
@@ -664,11 +664,31 @@ publicWidget.registry.hoverableDropdown = animations.Animation.extend({
|
||||
|
||||
publicWidget.registry.HeaderMainCollapse = publicWidget.Widget.extend({
|
||||
selector: 'header#top',
|
||||
disabledInEditableMode: false,
|
||||
events: {
|
||||
'show.bs.collapse #top_menu_collapse': '_onCollapseShow',
|
||||
'hidden.bs.collapse #top_menu_collapse': '_onCollapseHidden',
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start() {
|
||||
// This is a fix in stable to move the "call to action" button in the
|
||||
// navbar for the "boxed" header when the "off-canvas" mobile menu is
|
||||
// enabled. Without this, the "call to action" button is inaccessible in
|
||||
// the "off-canvas" mobile menu.
|
||||
this.offcanvasAndBoxedHeader = false;
|
||||
// If mobile menu is "off-canvas" and header template is "boxed".
|
||||
if (this.$target[0].querySelector('.o_offcanvas_menu_toggler')
|
||||
&& this.$target[0].querySelector('.o_header_boxed_background')) {
|
||||
this.navbarEl = this.$target[0].querySelector('#top_menu');
|
||||
this.callToActionEl = this.$target[0].querySelector('#oe_structure_header_boxed_2');
|
||||
this.offcanvasAndBoxedHeader = !!this.callToActionEl;
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -678,12 +698,20 @@ publicWidget.registry.HeaderMainCollapse = publicWidget.Widget.extend({
|
||||
*/
|
||||
_onCollapseShow() {
|
||||
this.el.classList.add('o_top_menu_collapse_shown');
|
||||
if (this.offcanvasAndBoxedHeader) {
|
||||
this.callToActionEl.classList.add('nav-item');
|
||||
this.navbarEl.append(this.callToActionEl);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onCollapseHidden() {
|
||||
this.el.classList.remove('o_top_menu_collapse_shown');
|
||||
if (this.offcanvasAndBoxedHeader) {
|
||||
this.callToActionEl.classList.remove('nav-item');
|
||||
this.navbarEl.after(this.callToActionEl);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ flectra.define('website.snippet.editor', function (require) {
|
||||
|
||||
const {qweb, _t, _lt} = require('web.core');
|
||||
const Dialog = require('web.Dialog');
|
||||
const publicWidget = require('web.public.widget');
|
||||
const weSnippetEditor = require('web_editor.snippet.editor');
|
||||
const wSnippetOptions = require('website.editor.snippets.options');
|
||||
|
||||
@@ -268,4 +269,83 @@ weSnippetEditor.Editor.include({
|
||||
'.o_bg_video_container',
|
||||
].join(','),
|
||||
});
|
||||
|
||||
// Edit mode customizations of public widgets.
|
||||
|
||||
publicWidget.registry.hoverableDropdown.include({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start() {
|
||||
if (this.editableMode) {
|
||||
this._onPageClick = this._onPageClick.bind(this);
|
||||
this.el.closest('#wrapwrap').addEventListener('click', this._onPageClick, {capture: true});
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
destroy() {
|
||||
if (this.editableMode) {
|
||||
this.el.closest('#wrapwrap').removeEventListener('click', this._onPageClick, {capture: true});
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Hides all opened dropdowns.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_hideDropdowns() {
|
||||
for (const toggleEl of this.el.querySelectorAll('.dropdown.show .dropdown-toggle')) {
|
||||
$(toggleEl).dropdown('hide');
|
||||
}
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Called when the page is clicked anywhere.
|
||||
* Closes the shown dropdown if the click is outside of it.
|
||||
*
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onPageClick(ev) {
|
||||
if (ev.target.closest('.dropdown.show')) {
|
||||
return;
|
||||
}
|
||||
this._hideDropdowns();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_onMouseEnter(ev) {
|
||||
if (this.editableMode) {
|
||||
// Do not handle hover if another dropdown is opened.
|
||||
if (this.el.querySelector('.dropdown.show')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_onMouseLeave(ev) {
|
||||
if (this.editableMode) {
|
||||
// Cancel handling from view mode.
|
||||
return;
|
||||
}
|
||||
this._super(...arguments);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -1958,6 +1958,15 @@ options.registry.HeaderNavbar = options.Class.extend({
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
async start() {
|
||||
await this._super(...arguments);
|
||||
// TODO Remove in master.
|
||||
const signInOptionEl = this.el.querySelector('[data-customize-website-views="portal.user_sign_in"]');
|
||||
signInOptionEl.dataset.noPreview = 'true';
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
@@ -143,7 +143,8 @@ function changePaddingSize(direction) {
|
||||
|
||||
/**
|
||||
* Click on the top right edit button
|
||||
* @param {*} position Where the purple arrow will show up
|
||||
*
|
||||
* @deprecated use `clickOnEditAndWaitEditMode` instead to avoid race condition
|
||||
*/
|
||||
function clickOnEdit(position = "bottom") {
|
||||
return {
|
||||
@@ -154,6 +155,23 @@ function clickOnEdit(position = "bottom") {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on the top right edit button and wait for the edit mode
|
||||
*
|
||||
* @param {string} position Where the purple arrow will show up
|
||||
*/
|
||||
function clickOnEditAndWaitEditMode(position = "bottom") {
|
||||
return [{
|
||||
content: _t("<b>Click Edit</b> to start designing your homepage."),
|
||||
trigger: "a[data-action=edit]",
|
||||
position: position,
|
||||
}, {
|
||||
content: "Check that we are in edit mode",
|
||||
trigger: '#oe_snippets.o_loaded',
|
||||
run: () => null, // it's a check
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple click on a snippet in the edition area
|
||||
* @param {*} snippet
|
||||
@@ -278,6 +296,7 @@ return {
|
||||
changeOption,
|
||||
changePaddingSize,
|
||||
clickOnEdit,
|
||||
clickOnEditAndWaitEditMode,
|
||||
clickOnSave,
|
||||
clickOnSnippet,
|
||||
clickOnText,
|
||||
|
||||
@@ -8,7 +8,7 @@ tour.register("snippet_image_gallery", {
|
||||
test: true,
|
||||
url: "/",
|
||||
}, [
|
||||
wTourUtils.clickOnEdit(),
|
||||
...wTourUtils.clickOnEditAndWaitEditMode(),
|
||||
wTourUtils.dragNDrop({
|
||||
id: "s_image_gallery",
|
||||
name: "Image Gallery",
|
||||
|
||||
@@ -794,7 +794,7 @@
|
||||
data-save-unit="rem"
|
||||
data-dependencies="!header_effect_scroll_opt"/>
|
||||
|
||||
<we-checkbox string="Show Sign In" data-customize-website-views="portal.user_sign_in" data-reload="/"/>
|
||||
<we-checkbox string="Show Sign In" data-customize-website-views="portal.user_sign_in" data-reload="/" data-no-preview="true"/>
|
||||
<we-select string="Language Selector" data-reload="/">
|
||||
<we-button data-name="header_language_selector_none_opt"
|
||||
data-customize-website-views="">None</we-button>
|
||||
|
||||
Reference in new issue
Block a user