[PATCH] Upstream patch - 30052022

This commit is contained in:
Parthiv Patel
2022-05-30 08:39:54 +00:00
parent 42afd7532f
commit d9aaed4062
28 changed files with 251 additions and 77 deletions

View File

@@ -7,4 +7,5 @@ from flectra import api, SUPERUSER_ID
def _update_street_format(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
env['res.partner'].search([])._compute_street_data()
specific_countries = env['res.country'].search([('street_format', '!=', '%(street_number)s/%(street_number2)s %(street_name)s')])
env['res.partner'].search([('country_id', 'in', specific_countries.ids)])._compute_street_data()

View File

@@ -6,6 +6,7 @@ const {
afterEach,
afterNextRender,
beforeEach,
isScrolledToBottom,
nextAnimationFrame,
start,
} = require('mail/static/src/utils/test_utils.js');
@@ -1028,7 +1029,7 @@ QUnit.test('[technical] chat window: scroll conservation on toggle home menu', a
thread &&
thread.model === 'mail.channel' &&
thread.id === 20 &&
scrollTop === messageList.scrollHeight - messageList.clientHeight
isScrolledToBottom(messageList)
);
},
});
@@ -1799,7 +1800,7 @@ QUnit.test('chat window with a thread: keep scroll position in message list on f
thread &&
thread.model === 'mail.channel' &&
thread.id === 20 &&
scrollTop === messageList.scrollHeight - messageList.clientHeight
isScrolledToBottom(messageList)
);
},
});
@@ -1885,9 +1886,8 @@ QUnit.test('chat window should scroll to the newly posted message just after pos
)
);
const messageList = document.querySelector('.o_MessageList');
assert.strictEqual(
messageList.scrollHeight - messageList.scrollTop,
messageList.clientHeight,
assert.ok(
isScrolledToBottom(messageList),
"chat window should scroll to the newly posted message just after posting it"
);
});
@@ -2038,7 +2038,7 @@ QUnit.test('[technical] chat window with a thread: keep scroll position in messa
thread &&
thread.model === 'mail.channel' &&
thread.id === 20 &&
scrollTop === messageList.scrollHeight - messageList.clientHeight
isScrolledToBottom(messageList)
);
},
});

View File

@@ -7,6 +7,7 @@ const {
afterEach,
afterNextRender,
beforeEach,
isScrolledToBottom,
nextAnimationFrame,
start,
} = require('mail/static/src/utils/test_utils.js');
@@ -1715,7 +1716,7 @@ QUnit.test('auto-scroll to bottom of thread', async function (assert) {
thread &&
thread.model === 'mail.channel' &&
thread.id === 20 &&
scrollTop === messageList.scrollHeight - messageList.clientHeight
isScrolledToBottom(messageList)
);
},
},
@@ -1728,9 +1729,8 @@ QUnit.test('auto-scroll to bottom of thread', async function (assert) {
"should have 25 messages"
);
const messageList = document.querySelector(`.o_Discuss_thread .o_ThreadView_messageList`);
assert.strictEqual(
messageList.scrollTop,
messageList.scrollHeight - messageList.clientHeight,
assert.ok(
isScrolledToBottom(messageList),
"should have scrolled to bottom of thread"
);
});
@@ -1763,7 +1763,7 @@ QUnit.test('load more messages from channel (auto-load on scroll)', async functi
thread &&
thread.model === 'mail.channel' &&
thread.id === 20 &&
scrollTop === messageList.scrollHeight - messageList.clientHeight
isScrolledToBottom(messageList)
);
},
},
@@ -1852,7 +1852,7 @@ QUnit.test('new messages separator [REQUIRE FOCUS]', async function (assert) {
thread &&
thread.model === 'mail.channel' &&
thread.id === 20 &&
scrollTop === messageList.scrollHeight - messageList.clientHeight
isScrolledToBottom(messageList)
);
},
},
@@ -1922,7 +1922,7 @@ QUnit.test('new messages separator [REQUIRE FOCUS]', async function (assert) {
thread &&
thread.model === 'mail.channel' &&
thread.id === 20 &&
scrollTop === messageList.scrollHeight - messageList.clientHeight
isScrolledToBottom(messageList)
);
},
});
@@ -1994,9 +1994,8 @@ QUnit.test('restore thread scroll position', async function (assert) {
.o_Discuss_thread
.o_ThreadView_messageList
`);
assert.strictEqual(
initialMessageList.scrollTop,
initialMessageList.scrollHeight - initialMessageList.clientHeight,
assert.ok(
isScrolledToBottom(initialMessageList),
"should have scrolled to bottom of channel 11 initially"
);
@@ -2039,7 +2038,7 @@ QUnit.test('restore thread scroll position', async function (assert) {
thread &&
thread.model === 'mail.channel' &&
thread.id === 12 &&
scrollTop === messageList.scrollHeight - messageList.clientHeight
isScrolledToBottom(messageList)
);
},
});
@@ -2102,14 +2101,13 @@ QUnit.test('restore thread scroll position', async function (assert) {
thread &&
thread.model === 'mail.channel' &&
thread.id === 12 &&
scrollTop === messageList.scrollHeight - messageList.clientHeight
isScrolledToBottom(messageList)
);
},
});
const messageList = document.querySelector('.o_ThreadView_messageList');
assert.strictEqual(
messageList.scrollTop,
messageList.scrollHeight - messageList.clientHeight,
assert.ok(
isScrolledToBottom(messageList),
"should have recovered scroll position of channel 12 (scroll to bottom)"
);
});
@@ -3687,7 +3685,7 @@ QUnit.test('load recent messages from thread (already loaded some old messages)'
thread &&
thread.model === 'mail.channel' &&
thread.id === 20 &&
scrollTop === messageList.scrollHeight - messageList.clientHeight
isScrolledToBottom(messageList)
);
},
});
@@ -4018,7 +4016,7 @@ QUnit.test('all messages in "Inbox" in "History" after marked all as read', asyn
thread.model === 'mail.box' &&
thread.id === 'inbox' &&
orderedMessages.length === 30 &&
scrollTop === messageList.scrollHeight - messageList.clientHeight
isScrolledToBottom(messageList)
);
},
},
@@ -4051,7 +4049,7 @@ QUnit.test('all messages in "Inbox" in "History" after marked all as read', asyn
thread.model === 'mail.box' &&
thread.id === 'history' &&
orderedMessages.length === 30 &&
scrollTop === messageList.scrollHeight - messageList.clientHeight
isScrolledToBottom(messageList)
);
},
});

View File

@@ -10,6 +10,7 @@ const {
beforeEach,
createRootComponent,
dragenterFiles,
isScrolledToBottom,
start,
} = require('mail/static/src/utils/test_utils.js');
@@ -833,9 +834,8 @@ QUnit.test('should scroll to bottom on receiving new message if the list is init
predicate: data => threadViewer === data.threadViewer,
});
const initialMessageList = document.querySelector('.o_ThreadView_messageList');
assert.strictEqual(
initialMessageList.scrollTop,
initialMessageList.scrollHeight - initialMessageList.clientHeight,
assert.ok(
isScrolledToBottom(initialMessageList),
"should have scrolled to bottom of channel 20 initially"
);
@@ -857,9 +857,8 @@ QUnit.test('should scroll to bottom on receiving new message if the list is init
predicate: data => threadViewer === data.threadViewer,
});
const messageList = document.querySelector('.o_ThreadView_messageList');
assert.strictEqual(
messageList.scrollTop,
messageList.scrollHeight - messageList.clientHeight,
assert.ok(
isScrolledToBottom(messageList),
"should scroll to bottom on receiving new message because the list is initially scrolled to bottom"
);
});
@@ -904,9 +903,8 @@ QUnit.test('should not scroll on receiving new message if the list is initially
predicate: data => threadViewer === data.threadViewer,
});
const initialMessageList = document.querySelector('.o_ThreadView_messageList');
assert.strictEqual(
initialMessageList.scrollTop,
initialMessageList.scrollHeight - initialMessageList.clientHeight,
assert.ok(
isScrolledToBottom(initialMessageList),
"should have scrolled to bottom of channel 20 initially"
);

View File

@@ -747,6 +747,23 @@ function pasteFiles(el, files) {
el.dispatchEvent(ev);
}
//------------------------------------------------------------------------------
// Public: DOM utilities
//------------------------------------------------------------------------------
/**
* Determine if a DOM element has been totally scrolled
*
* A 1px margin of error is given to accomodate subpixel rounding issues and
* Element.scrollHeight value being either int or decimal
*
* @param {DOM.Element} el
* @returns {boolean}
*/
function isScrolledToBottom(el) {
return Math.abs(el.scrollHeight - el.clientHeight - el.scrollTop) <= 1;
}
//------------------------------------------------------------------------------
// Export
//------------------------------------------------------------------------------
@@ -758,6 +775,7 @@ return {
createRootComponent,
dragenterFiles,
dropFiles,
isScrolledToBottom,
nextAnimationFrame,
nextTick,
pasteFiles,

View File

@@ -6,6 +6,7 @@ const {
afterEach,
afterNextRender,
beforeEach,
isScrolledToBottom,
nextAnimationFrame,
start,
} = require('mail/static/src/utils/test_utils.js');
@@ -846,9 +847,8 @@ QUnit.test('Form view not scrolled when switching record', async function (asser
'scroll'
);
});
assert.strictEqual(
controllerContentEl.scrollTop,
controllerContentEl.scrollHeight - controllerContentEl.clientHeight,
assert.ok(
isScrolledToBottom(controllerContentEl),
"The controller container should be scrolled to its bottom"
);

View File

@@ -143,8 +143,9 @@ class MrpBom(models.Model):
res = super().copy(default)
for bom_line in res.bom_line_ids:
if bom_line.operation_id:
operation = res.operation_ids.filtered(lambda op: op.name == bom_line.operation_id.name and op.workcenter_id == bom_line.operation_id.workcenter_id)
bom_line.operation_id = operation
operation = res.operation_ids.filtered(lambda op: op._get_comparison_values() == bom_line.operation_id._get_comparison_values())
# Two operations could have the same values so we take the first one
bom_line.operation_id = operation[:1]
return res
@api.model

View File

@@ -77,3 +77,9 @@ class MrpRoutingWorkcenter(models.Model):
count_data = dict((item['operation_id'][0], item['operation_id_count']) for item in data)
for operation in self:
operation.workorder_count = count_data.get(operation.id, 0)
def _get_comparison_values(self):
if not self:
return False
self.ensure_one()
return tuple(self[key] for key in ('name', 'company_id', 'workcenter_id', 'time_mode', 'time_cycle_manual'))

View File

@@ -3,7 +3,7 @@
from flectra import _, api, fields, models
from flectra.tools.float_utils import float_is_zero
from flectra.osv.expression import AND
class StockWarehouseOrderpoint(models.Model):
_inherit = 'stock.warehouse.orderpoint'
@@ -13,12 +13,12 @@ class StockWarehouseOrderpoint(models.Model):
'mrp.bom', string='Bill of Materials', check_company=True,
domain="[('type', '=', 'normal'), '&', '|', ('company_id', '=', company_id), ('company_id', '=', False), '|', ('product_id', '=', product_id), '&', ('product_id', '=', False), ('product_tmpl_id', '=', product_tmpl_id)]")
def _get_replenishment_order_notification(self, written_after):
def _get_replenishment_order_notification(self):
self.ensure_one()
production = self.env['mrp.production'].search([
('orderpoint_id', 'in', self.ids),
('write_date', '>', written_after)
], order='create_date desc', limit=1)
domain = [('orderpoint_id', 'in', self.ids)]
if self.env.context.get('written_date'):
domain = AND([domain, [('write_date', '>', self.env.context.get('written_after'))]])
production = self.env['mrp.production'].search(domain, limit=1)
if production:
action = self.env.ref('mrp.action_mrp_production_form')
return {
@@ -34,7 +34,7 @@ class StockWarehouseOrderpoint(models.Model):
'sticky': False,
}
}
return super()._get_replenishment_order_notification(written_after)
return super()._get_replenishment_order_notification()
@api.depends('route_id')
def _compute_show_bom(self):

View File

@@ -794,7 +794,7 @@ class PosSession(models.Model):
# may arise in 'Round Globally'.
check_refund = lambda x: x.qty * x.price_unit < 0
is_refund = check_refund(order_line)
tax_data = tax_ids.compute_all(price_unit=price, quantity=abs(order_line.qty), currency=self.currency_id, is_refund=is_refund)
tax_data = tax_ids.with_context(force_sign=sign).compute_all(price_unit=price, quantity=abs(order_line.qty), currency=self.currency_id, is_refund=is_refund)
taxes = tax_data['taxes']
# For Cash based taxes, use the account from the repartition line immediately as it has been paid already
for tax in taxes:

View File

@@ -6,6 +6,7 @@ flectra.define('point_of_sale.ClientListScreen', function(require) {
const Registries = require('point_of_sale.Registries');
const { useListener } = require('web.custom_hooks');
const { isRpcError } = require('point_of_sale.utils');
const { useAsyncLockedMethod } = require('point_of_sale.custom_hooks');
/**
* Render this screen using `showTempScreen` to select client.
@@ -25,9 +26,10 @@ flectra.define('point_of_sale.ClientListScreen', function(require) {
class ClientListScreen extends PosComponent {
constructor() {
super(...arguments);
this.lockedSaveChanges = useAsyncLockedMethod(this.saveChanges);
useListener('click-save', () => this.env.bus.trigger('save-customer'));
useListener('click-edit', () => this.editClient());
useListener('save-changes', this.saveChanges);
useListener('save-changes', this.lockedSaveChanges);
// We are not using useState here because the object
// passed to useState converts the object and its contents

View File

@@ -3,7 +3,7 @@ flectra.define('point_of_sale.PaymentScreen', function (require) {
const { parse } = require('web.field_utils');
const PosComponent = require('point_of_sale.PosComponent');
const { useErrorHandlers } = require('point_of_sale.custom_hooks');
const { useErrorHandlers, useAsyncLockedMethod } = require('point_of_sale.custom_hooks');
const NumberBuffer = require('point_of_sale.NumberBuffer');
const { useListener } = require('web.custom_hooks');
const Registries = require('point_of_sale.Registries');
@@ -20,6 +20,7 @@ flectra.define('point_of_sale.PaymentScreen', function (require) {
useListener('send-payment-cancel', this._sendPaymentCancel);
useListener('send-payment-reverse', this._sendPaymentReverse);
useListener('send-force-done', this._sendForceDone);
this.lockedValidateOrder = useAsyncLockedMethod(this.validateOrder);
NumberBuffer.use({
// The numberBuffer listens to this event to update its state.
// Basically means 'update the buffer when this event is triggered'
@@ -306,7 +307,7 @@ flectra.define('point_of_sale.PaymentScreen', function (require) {
' ' +
this.env._t('? Clicking "Confirm" will validate the payment.'),
}).then(({ confirmed }) => {
if (confirmed) this.validateOrder(true);
if (confirmed) this.lockedValidateOrder(true);
});
return false;
}

View File

@@ -145,5 +145,21 @@ flectra.define('point_of_sale.custom_hooks', function (require) {
});
}
return { useErrorHandlers, useAutoFocusToLast, onChangeOrder, useBarcodeReader };
function useAsyncLockedMethod(method) {
const component = Component.current;
let called = false;
return async (...args) => {
if (called) {
return;
}
try {
called = true;
await method.call(component, ...args);
} finally {
called = false;
}
};
}
return { useErrorHandlers, useAutoFocusToLast, onChangeOrder, useBarcodeReader, useAsyncLockedMethod };
});

View File

@@ -1403,6 +1403,8 @@ exports.PosModel = Backbone.Model.extend({
if(base < 0){
base = -base;
sign = -1;
} else if(utils.float_is_zero(base, this.currency.decimals) && quantity < 0){
sign = -1
}
var total_included_checkpoints = {};
@@ -2737,7 +2739,9 @@ exports.Order = Backbone.Model.extend({
var orderlines = json.lines;
for (var i = 0; i < orderlines.length; i++) {
var orderline = orderlines[i][2];
this.add_orderline(new exports.Orderline({}, {pos: this.pos, order: this, json: orderline}));
if(this.pos.db.get_product_by_id(orderline.product_id)){
this.add_orderline(new exports.Orderline({}, {pos: this.pos, order: this, json: orderline}));
}
}
var paymentlines = json.statement_ids;

View File

@@ -103,3 +103,31 @@ flectra.define('point_of_sale.tour.ProductScreen', function (require) {
Tour.register('ProductScreenTour', { test: true, url: '/pos/ui' }, getSteps());
});
flectra.define('point_of_sale.tour.FixedPriceNegativeQty', function (require) {
'use strict';
const { ProductScreen } = require('point_of_sale.tour.ProductScreenTourMethods');
const { PaymentScreen } = require('point_of_sale.tour.PaymentScreenTourMethods');
const { ReceiptScreen } = require('point_of_sale.tour.ReceiptScreenTourMethods');
const { getSteps, startSteps } = require('point_of_sale.tour.utils');
var Tour = require('web_tour.tour');
startSteps();
ProductScreen.do.clickHomeCategory();
ProductScreen.do.clickDisplayedProduct('Zero Amount Product');
ProductScreen.check.selectedOrderlineHas('Zero Amount Product', '1.0', '1.0');
ProductScreen.do.pressNumpad('+/- 1');
ProductScreen.check.selectedOrderlineHas('Zero Amount Product', '-1.0', '-1.0');
ProductScreen.do.clickPayButton();
PaymentScreen.do.clickPaymentMethod('Bank');
PaymentScreen.check.remainingIs('0.00');
PaymentScreen.do.clickValidate();
ReceiptScreen.check.receiptIsThere();
Tour.register('FixedTaxNegativeQty', { test: true, url: '/pos/ui' }, getSteps());
});

View File

@@ -523,3 +523,60 @@ class TestUi(TestPointOfSaleHttpCommon):
def test_05_ticket_screen(self):
self.main_pos_config.open_session_cb(check_coa=False)
self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'TicketScreenTour', login="admin")
def test_fixed_tax_negative_qty(self):
""" Assert the negative amount of a negative-quantity orderline
with zero-amount product with fixed tax.
"""
# setup the zero-amount product
tax_received_account = self.env['account.account'].create({
'name': 'TAX_BASE',
'code': 'TBASE',
'user_type_id': self.env.ref('account.data_account_type_current_assets').id,
'company_id': self.env.company.id,
})
fixed_tax = self.env['account.tax'].create({
'name': 'fixed amount tax',
'amount_type': 'fixed',
'amount': 1,
'invoice_repartition_line_ids': [
(0, 0, {
'factor_percent': 100,
'repartition_type': 'base',
}),
(0, 0, {
'factor_percent': 100,
'repartition_type': 'tax',
'account_id': tax_received_account.id,
}),
],
})
zero_amount_product = self.env['product.product'].create({
'name': 'Zero Amount Product',
'available_in_pos': True,
'list_price': 0,
'taxes_id': [(6, 0, [fixed_tax.id])],
})
# Make an order with the zero-amount product from the frontend.
# We need to do this because of the fix in the "compute_all" port.
self.main_pos_config.write({'iface_tax_included': 'total'})
self.main_pos_config.open_session_cb(check_coa=False)
self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'FixedTaxNegativeQty', login="admin")
pos_session = self.main_pos_config.current_session_id
# Close the session and check the session journal entry.
pos_session.action_pos_session_validate()
lines = pos_session.move_id.line_ids.sorted('balance')
# order in the tour is paid using the bank payment method.
bank_pm = self.main_pos_config.payment_method_ids.filtered(lambda pm: pm.name == 'Bank')
self.assertEqual(lines[0].account_id, bank_pm.receivable_account_id)
self.assertAlmostEqual(lines[0].balance, -1)
self.assertEqual(lines[1].account_id, zero_amount_product.categ_id.property_account_income_categ_id)
self.assertAlmostEqual(lines[1].balance, 0)
self.assertEqual(lines[2].account_id, tax_received_account)
self.assertAlmostEqual(lines[2].balance, 1)

View File

@@ -576,3 +576,45 @@ class TestPoSProductsWithTax(TestPoSCommon):
{'account_id': self.tax_received_account.id, 'balance': 15.14, 'tax_ids': [], 'tax_tag_ids': self.tax_tag_refund_tax.ids},
{'account_id': self.sale_account.id, 'balance': 72.07, 'tax_ids': tax_21_incl.ids, 'tax_tag_ids': self.tax_tag_refund_base.ids},
])
def test_fixed_tax_positive_qty(self):
fixed_tax = self.env['account.tax'].create({
'name': 'fixed amount tax',
'amount_type': 'fixed',
'amount': 1,
'invoice_repartition_line_ids': [
(0, 0, {
'factor_percent': 100,
'repartition_type': 'base',
'tag_ids': [(6, 0, self.tax_tag_invoice_base.ids)],
}),
(0, 0, {
'factor_percent': 100,
'repartition_type': 'tax',
'account_id': self.tax_received_account.id,
'tag_ids': [(6, 0, self.tax_tag_invoice_tax.ids)],
}),
],
})
zero_amount_product = self.env['product.product'].create({
'name': 'Zero Amount Product',
'available_in_pos': True,
'list_price': 0,
'taxes_id': [(6, 0, [fixed_tax.id])],
})
self.open_new_session()
self.env['pos.order'].create_from_ui([self.create_ui_order_data([
(zero_amount_product, 1),
])])
self.pos_session.action_pos_session_validate()
lines = self.pos_session.move_id.line_ids.sorted('balance')
self.assertRecordValues(lines, [
{'account_id': self.tax_received_account.id, 'balance': -1},
{'account_id': self.sale_account.id, 'balance': 0},
{'account_id': self.pos_receivable_account.id, 'balance': 1},
])

View File

@@ -32,8 +32,8 @@ class SaleReport(models.Model):
CASE WHEN pos.state != 'invoiced' THEN sum(l.qty) ELSE 0 END AS qty_to_invoice,
SUM(l.price_subtotal_incl) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS price_total,
SUM(l.price_subtotal) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS price_subtotal,
(CASE WHEN pos.state != 'invoiced' THEN SUM(l.price_subtotal_incl) ELSE 0 END) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS amount_to_invoice,
(CASE WHEN pos.state = 'invoiced' THEN SUM(l.price_subtotal_incl) ELSE 0 END) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS amount_invoiced,
(CASE WHEN pos.state != 'invoiced' THEN SUM(l.price_subtotal) ELSE 0 END) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS amount_to_invoice,
(CASE WHEN pos.state = 'invoiced' THEN SUM(l.price_subtotal) ELSE 0 END) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS amount_invoiced,
count(*) AS nbr,
pos.name AS name,
pos.date_order AS date,

View File

@@ -82,7 +82,7 @@
<field name="date_start" optional="show"/>
<field name="date_end" optional="show"/>
<field name="applied_on" invisible="1"/>
<field name="company_id" groups="base.group_multi_company" optional="show" options="{'no_create_edit':1, 'no_open': 1}"/>
<field name="company_id" groups="base.group_multi_company" optional="show" options="{'no_create':1, 'no_open': 1}"/>
</tree>
</field>
</record>

View File

@@ -14,7 +14,7 @@
<field name="name" string="Product Name"/>
<field name="default_code" optional="show"/>
<field name="barcode" optional="hide" attrs="{'readonly': [('product_variant_count', '>', 1)]}"/>
<field name="company_id" options="{'no_create_edit': True}"
<field name="company_id" options="{'no_create': True}"
groups="base.group_multi_company" optional="hide"/>
<field name="list_price" string="Sales Price" widget='monetary' options="{'currency_field': 'currency_id'}" optional="show" decoration-muted="not sale_ok"/>
<field name="standard_price" widget='monetary' options="{'currency_field': 'cost_currency_id'}" optional="show" readonly="1"/>

View File

@@ -250,7 +250,7 @@ QUnit.module('section_and_note: purchase_product_matrix', {
// move first row below second
const $firstHandle = form.$('.o_data_row:nth(0) .o_row_handle');
const $secondHandle = form.$('.o_data_row:nth(1) .o_row_handle');
await testUtils.dom.dragAndDrop($firstHandle, $secondHandle);
await testUtils.dom.dragAndDrop($firstHandle, $secondHandle, { position: 'bottom' });
assert.strictEqual(form.$('.o_data_row').text(), 'TableChair');

View File

@@ -4,7 +4,7 @@
from flectra import api, fields, models, _
from flectra.tools.float_utils import float_round, float_is_zero
from flectra.exceptions import UserError
from flectra.osv.expression import AND
class StockPicking(models.Model):
_inherit = 'stock.picking'
@@ -237,12 +237,12 @@ class Orderpoint(models.Model):
return result
def _get_replenishment_order_notification(self, written_after):
def _get_replenishment_order_notification(self):
self.ensure_one()
order = self.env['purchase.order.line'].search([
('orderpoint_id', 'in', self.ids),
('write_date', '>', written_after)
], limit=1).order_id
domain = [('orderpoint_id', 'in', self.ids)]
if self.env.context.get('written_date'):
domain = AND([domain, [('write_date', '>', self.env.context.get('written_after'))]])
order = self.env['purchase.order.line'].search(domain, limit=1).order_id
if order:
action = self.env.ref('purchase.action_rfq_form')
return {
@@ -258,7 +258,7 @@ class Orderpoint(models.Model):
'sticky': False,
}
}
return super()._get_replenishment_order_notification(written_after)
return super()._get_replenishment_order_notification()
def _prepare_procurement_values(self, date=False, group=False):
values = super()._prepare_procurement_values(date=date, group=group)

View File

@@ -90,7 +90,7 @@ flectra.define('sale.product_configurator_tests', function (require) {
// move first row below second
const $firstHandle = form.$('.o_data_row:nth(0) .o_row_handle');
const $secondHandle = form.$('.o_data_row:nth(1) .o_row_handle');
await testUtils.dom.dragAndDrop($firstHandle, $secondHandle);
await testUtils.dom.dragAndDrop($firstHandle, $secondHandle, { position: 'bottom' });
assert.strictEqual(form.$('.o_data_row').text(), 'TableChair');

View File

@@ -298,7 +298,7 @@ QUnit.module('Product Configurator', {
// move first row below second
const $firstHandle = form.$('.o_data_row:nth(0) .o_row_handle');
const $secondHandle = form.$('.o_data_row:nth(1) .o_row_handle');
await testUtils.dom.dragAndDrop($firstHandle, $secondHandle);
await testUtils.dom.dragAndDrop($firstHandle, $secondHandle, { position: 'bottom' });
assert.strictEqual(form.$('.o_data_row').text(), 'Customizable Desk (2)Customizable Desk (1)');

View File

@@ -216,7 +216,7 @@ class StockWarehouseOrderpoint(models.Model):
self._procure_orderpoint_confirm(company_id=self.env.company)
notification = False
if len(self) == 1:
notification = self._get_replenishment_order_notification(now)
notification = self.with_context(written_after=now)._get_replenishment_order_notification()
# Forced to call compute quantity because we don't have a link.
self._compute_qty()
self.filtered(lambda o: o.create_uid.id == SUPERUSER_ID and o.qty_to_order <= 0.0 and o.trigger == 'manual').unlink()
@@ -425,12 +425,12 @@ class StockWarehouseOrderpoint(models.Model):
'trigger': 'manual',
}
def _get_replenishment_order_notification(self, written_after):
def _get_replenishment_order_notification(self):
self.ensure_one()
move = self.env['stock.move'].search([
('orderpoint_id', 'in', self.ids),
('write_date', '>', written_after)
], limit=1)
domain = [('orderpoint_id', 'in', self.ids)]
if self.env.context.get('written_date'):
domain = expression.AND([domain, [('write_date', '>', self.env.context.get('written_after'))]])
move = self.env['stock.move'].search(domain, limit=1)
if move.picking_id:
return {
'type': 'ir.actions.client',

View File

@@ -36,7 +36,9 @@ flectra.define('web.FavoriteMenu', function (require) {
get icon() {
return FACET_ICONS.favorite;
}
get dialogTitle() {
return this.env._t("Warning");
}
/**
* @override
*/

View File

@@ -184,7 +184,7 @@
</xpath>
<xpath expr="//div[1]" position="inside">
<Dialog t-if="state.deletedFavorite"
title="'Warning'"
title="dialogTitle"
size="'medium'"
t-on-dialog-closed="state.deletedFavorite = false"
>

View File

@@ -2627,8 +2627,8 @@ QUnit.module('Views', {
assert.strictEqual(list.$('table').width(), list.$('.o_list_view').width());
const largeCells = list.$('.o_data_cell.large');
assert.strictEqual(largeCells[0].offsetWidth, largeCells[1].offsetWidth);
assert.strictEqual(largeCells[1].offsetWidth, largeCells[2].offsetWidth);
assert.ok(Math.abs(largeCells[0].offsetWidth - largeCells[1].offsetWidth) <= 1);
assert.ok(Math.abs(largeCells[1].offsetWidth - largeCells[2].offsetWidth) <= 1);
assert.ok(list.$('.o_data_cell:not(.large)')[0].offsetWidth < largeCells[0].offsetWidth);
list.destroy();
@@ -3119,7 +3119,7 @@ QUnit.module('Views', {
const text = list.el.querySelector('th[data-name="text"]');
const textWidth = Math.ceil(text.getBoundingClientRect().width);
assert.strictEqual(fooWidth, textWidth, "both columns should have been given the same width");
assert.ok(Math.abs(fooWidth - textWidth) <= 1, "both columns should have been given the same width");
const firstRowHeight = list.$('.o_data_row:nth(0)')[0].offsetHeight;
const secondRowHeight = list.$('.o_data_row:nth(1)')[0].offsetHeight;