mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2026-10-25 07:27:39 -05:00
[PATCH] Upstream patch - 19032023
This commit is contained in:
1 parent
63dca856d9
commit
90effe3bad
9 files changed
+183
-13
No files matched your search
@@ -610,18 +610,37 @@ class AccountReconcileModel(models.Model):
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
# On big databases, it is possible that some setups will create huge queries when trying to apply reconciliation models.
|
||||
# In such cases, this query might take a very long time to run, essentially eating up all the available CPU, and proof
|
||||
# impossible to kill, because of the type of operations ran by SQL. To alleviate that, we introduce the config parameter below,
|
||||
# which essentially allows cutting the list of statement lines to match into slices, and running the matching in multiple queries.
|
||||
# This way, we avoid server overload, giving the ability to kill the process if takes too long.
|
||||
slice_size = len(st_lines_with_partner)
|
||||
slice_size_param = self.env['ir.config_parameter'].sudo().get_param('account.reconcile_model_forced_slice_size')
|
||||
if slice_size_param:
|
||||
converted_param = int(slice_size_param)
|
||||
if converted_param > 0:
|
||||
slice_size = converted_param
|
||||
|
||||
treatment_slices = []
|
||||
slice_start = 0
|
||||
while slice_start < len(st_lines_with_partner):
|
||||
slice_end = slice_start + slice_size
|
||||
treatment_slices.append(st_lines_with_partner[slice_start:slice_end])
|
||||
slice_start = slice_end
|
||||
|
||||
treatment_map = {
|
||||
'invoice_matching': lambda x: x._get_invoice_matching_query(st_lines_with_partner, excluded_ids),
|
||||
'writeoff_suggestion': lambda x: x._get_writeoff_suggestion_query(st_lines_with_partner, excluded_ids),
|
||||
'invoice_matching': lambda rec_model, slice: rec_model._get_invoice_matching_query(slice, excluded_ids),
|
||||
'writeoff_suggestion': lambda rec_model, slice: rec_model._get_writeoff_suggestion_query(slice, excluded_ids),
|
||||
}
|
||||
|
||||
query_generator = treatment_map[self.rule_type]
|
||||
query, params = query_generator(self)
|
||||
self._cr.execute(query, params)
|
||||
|
||||
rslt = defaultdict(lambda: [])
|
||||
for candidate_dict in self._cr.dictfetchall():
|
||||
rslt[candidate_dict['id']].append(candidate_dict)
|
||||
for treatment_slice in treatment_slices:
|
||||
query_generator = treatment_map[self.rule_type]
|
||||
query, params = query_generator(self, treatment_slice)
|
||||
self._cr.execute(query, params)
|
||||
|
||||
for candidate_dict in self._cr.dictfetchall():
|
||||
rslt[candidate_dict['id']].append(candidate_dict)
|
||||
|
||||
return rslt
|
||||
|
||||
|
||||
@@ -578,7 +578,9 @@ class MrpProduction(models.Model):
|
||||
if not self.product_id:
|
||||
self.bom_id = False
|
||||
elif not self.bom_id or self.bom_id.product_tmpl_id != self.product_tmpl_id or (self.bom_id.product_id and self.bom_id.product_id != self.product_id):
|
||||
bom = self.env['mrp.bom']._bom_find(product=self.product_id, picking_type=self.picking_type_id, company_id=self.company_id.id, bom_type='normal')
|
||||
picking_type_id = self._context.get('default_picking_type_id')
|
||||
picking_type = picking_type_id and self.env['stock.picking.type'].browse(picking_type_id)
|
||||
bom = self.env['mrp.bom']._bom_find(product=self.product_id, picking_type=picking_type, company_id=self.company_id.id, bom_type='normal')
|
||||
if bom:
|
||||
self.bom_id = bom.id
|
||||
self.product_qty = self.bom_id.product_qty
|
||||
@@ -606,7 +608,9 @@ class MrpProduction(models.Model):
|
||||
self.product_uom_id = self.bom_id and self.bom_id.product_uom_id.id or self.product_id.uom_id.id
|
||||
self.move_raw_ids = [(2, move.id) for move in self.move_raw_ids.filtered(lambda m: m.bom_line_id)]
|
||||
self.move_finished_ids = [(2, move.id) for move in self.move_finished_ids]
|
||||
self.picking_type_id = self.bom_id.picking_type_id or self.picking_type_id
|
||||
picking_type_id = self._context.get('default_picking_type_id')
|
||||
picking_type = picking_type_id and self.env['stock.picking.type'].browse(picking_type_id)
|
||||
self.picking_type_id = picking_type or self.bom_id.picking_type_id or self.picking_type_id
|
||||
|
||||
@api.onchange('date_planned_start', 'product_id')
|
||||
def _onchange_date_planned_start(self):
|
||||
|
||||
@@ -2506,3 +2506,65 @@ class TestMrpOrder(TestMrpCommon):
|
||||
raw.product_uom_qty = 1.25
|
||||
|
||||
self.assertEqual(mo.move_raw_ids.quantity_done, 1.25)
|
||||
|
||||
def test_onchange_bom_ids_and_picking_type(self):
|
||||
warehouse01 = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
|
||||
warehouse02, warehouse03 = self.env['stock.warehouse'].create([
|
||||
{'name': 'Second Warehouse', 'code': 'WH02'},
|
||||
{'name': 'Third Warehouse', 'code': 'WH03'},
|
||||
])
|
||||
|
||||
finished_product = self.env['product.product'].create({'name': 'finished product'})
|
||||
bom_wh01, bom_wh02 = self.env['mrp.bom'].create([{
|
||||
'product_id': finished_product.id,
|
||||
'product_tmpl_id': finished_product.product_tmpl_id.id,
|
||||
'product_uom_id': self.uom_unit.id,
|
||||
'product_qty': 1.0,
|
||||
'bom_line_ids': [(0, 0, {'product_id': self.product_0.id, 'product_qty': 1})],
|
||||
'picking_type_id': wh.manu_type_id.id,
|
||||
'sequence': wh.id,
|
||||
} for wh in [warehouse01, warehouse02]])
|
||||
|
||||
# Prioritize BoM of WH02
|
||||
bom_wh01.sequence = bom_wh02.sequence + 1
|
||||
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = finished_product
|
||||
self.assertEqual(mo_form.bom_id, bom_wh02, 'Should select the first BoM in the list, whatever the picking type is')
|
||||
self.assertEqual(mo_form.picking_type_id, warehouse02.manu_type_id)
|
||||
|
||||
mo_form.bom_id = bom_wh01
|
||||
self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should be adapted because of the found BoM')
|
||||
|
||||
mo_form.bom_id = bom_wh02
|
||||
self.assertEqual(mo_form.picking_type_id, warehouse02.manu_type_id, 'Should be adapted because of the found BoM')
|
||||
|
||||
mo_form.picking_type_id = warehouse01.manu_type_id
|
||||
self.assertEqual(mo_form.bom_id, bom_wh02, 'Should not change')
|
||||
self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should not change')
|
||||
|
||||
mo_form.picking_type_id = warehouse03.manu_type_id
|
||||
mo_form.bom_id = bom_wh01
|
||||
self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should be adapted because of the found BoM '
|
||||
'(the selected picking type should be ignored)')
|
||||
|
||||
mo_form = Form(self.env['mrp.production'].with_context(default_picking_type_id=warehouse03.manu_type_id.id))
|
||||
mo_form.product_id = finished_product
|
||||
self.assertFalse(mo_form.bom_id, 'Should not find any BoM, because of the defined picking type')
|
||||
self.assertEqual(mo_form.picking_type_id, warehouse03.manu_type_id)
|
||||
|
||||
mo_form = Form(self.env['mrp.production'].with_context(default_picking_type_id=warehouse01.manu_type_id.id))
|
||||
mo_form.product_id = finished_product
|
||||
self.assertEqual(mo_form.bom_id, bom_wh01, 'Should select the BoM that matches the default picking type')
|
||||
self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should be the default one')
|
||||
|
||||
mo_form.bom_id = bom_wh02
|
||||
self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should not change, because of default value')
|
||||
|
||||
mo_form.picking_type_id = warehouse02.manu_type_id
|
||||
self.assertEqual(mo_form.bom_id, bom_wh02, 'Should not change')
|
||||
self.assertEqual(mo_form.picking_type_id, warehouse02.manu_type_id, 'Should not change')
|
||||
|
||||
mo_form.picking_type_id = warehouse02.manu_type_id
|
||||
mo_form.bom_id = bom_wh02
|
||||
self.assertEqual(mo_form.picking_type_id, warehouse01.manu_type_id, 'Should be adapted because of the default value')
|
||||
@@ -1082,6 +1082,36 @@ class TestSaleStock(TestSaleCommon, ValuationReconciliationTestCommon):
|
||||
self.assertEqual(so.order_line[1].qty_delivered, 1)
|
||||
self.assertEqual(so.order_line[1].product_uom.id, uom_km_id)
|
||||
|
||||
def test_multiple_returns(self):
|
||||
# Creates a sale order for 10 products.
|
||||
sale_order = self._get_new_sale_order()
|
||||
# Valids the sale order, then valids the delivery.
|
||||
sale_order.action_confirm()
|
||||
picking = sale_order.picking_ids
|
||||
picking.move_lines.write({'quantity_done': 10})
|
||||
picking.button_validate()
|
||||
|
||||
# Creates a return from the delivery picking.
|
||||
return_picking_form = Form(self.env['stock.return.picking']
|
||||
.with_context(active_ids=picking.ids, active_id=picking.id,
|
||||
active_model='stock.picking'))
|
||||
return_wizard = return_picking_form.save()
|
||||
# Check that the correct quantity is set on the retrun
|
||||
self.assertEqual(return_wizard.product_return_moves.quantity, 10)
|
||||
return_wizard.product_return_moves.quantity = 2
|
||||
# Valids the return picking.
|
||||
res = return_wizard.create_returns()
|
||||
return_picking = self.env['stock.picking'].browse(res['res_id'])
|
||||
return_picking.move_lines.write({'quantity_done': 2})
|
||||
return_picking.button_validate()
|
||||
|
||||
# Creates a second return from the delivery picking.
|
||||
return_picking_form = Form(self.env['stock.return.picking']
|
||||
.with_context(active_ids=picking.ids, active_id=picking.id,
|
||||
active_model='stock.picking'))
|
||||
return_wizard = return_picking_form.save()
|
||||
# Check that the remaining quantity is set on the retrun
|
||||
self.assertEqual(return_wizard.product_return_moves.quantity, 8)
|
||||
|
||||
class TestSaleStockOnly(SavepointCase):
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ class ReturnPicking(models.TransientModel):
|
||||
def _prepare_stock_return_picking_line_vals_from_move(self, stock_move):
|
||||
quantity = stock_move.product_qty
|
||||
for move in stock_move.move_dest_ids:
|
||||
if move.origin_returned_move_id and move.origin_returned_move_id != stock_move:
|
||||
if not move.origin_returned_move_id or move.origin_returned_move_id != stock_move:
|
||||
continue
|
||||
if move.state in ('partially_available', 'assigned'):
|
||||
quantity -= sum(move.move_line_ids.mapped('product_qty'))
|
||||
|
||||
@@ -931,7 +931,7 @@ var SnippetEditor = Widget.extend({
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.trigger_up('request_history_undo_record', {$target: this.$target});
|
||||
this.removeSnippet();
|
||||
this.trigger_up('snippet_edition_request', {exec: this.removeSnippet.bind(this)});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
flectra.define("website.tour.snippet_image_gallery", function (require) {
|
||||
"use strict";
|
||||
|
||||
const tour = require("web_tour.tour");
|
||||
const wTourUtils = require("website.tour_utils");
|
||||
|
||||
tour.register("snippet_image_gallery", {
|
||||
test: true,
|
||||
url: "/",
|
||||
}, [
|
||||
wTourUtils.clickOnEdit(),
|
||||
wTourUtils.dragNDrop({
|
||||
id: "s_image_gallery",
|
||||
name: "Image Gallery",
|
||||
}), {
|
||||
content: "Click on the first new image",
|
||||
trigger: ".o_select_media_dialog img[title='s_default_image.jpg']",
|
||||
}, {
|
||||
content: "Click on the second new image",
|
||||
trigger: ".o_select_media_dialog img[title='s_default_image2.jpg']",
|
||||
}, {
|
||||
content: "Click on Add",
|
||||
trigger: "button:has(span:contains('Add'))",
|
||||
}, {
|
||||
content: "Click on the image of the Image Gallery snippet",
|
||||
trigger: ".s_image_gallery .carousel-item.active img",
|
||||
}, {
|
||||
content: "Click on Remove Block",
|
||||
trigger: ".o_we_customize_panel we-title:has(span:contains('Image Gallery')) we-button[title='Remove Block']",
|
||||
}, {
|
||||
content: "Check that the Image Gallery snippet has been removed",
|
||||
trigger: "#wrap:not(:has(.s_image_gallery))",
|
||||
run: () => null,
|
||||
}]);
|
||||
});
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
import flectra
|
||||
import flectra.tests
|
||||
from flectra.tests.common import HOST
|
||||
from flectra.tools import config
|
||||
|
||||
|
||||
@flectra.tests.common.tagged('post_install', '-at_install', 'website_snippets')
|
||||
@@ -13,3 +15,20 @@ class TestSnippets(flectra.tests.HttpCase):
|
||||
|
||||
def test_02_countdown_preview(self):
|
||||
self.start_tour("/?enable_editor=1", "snippet_countdown", login='admin')
|
||||
|
||||
def test_03_snippet_image_gallery(self):
|
||||
IrAttachment = self.env['ir.attachment']
|
||||
base = "http://%s:%s" % (HOST, config['http_port'])
|
||||
IrAttachment.create({
|
||||
'public': True,
|
||||
'name': 's_default_image.jpg',
|
||||
'type': 'url',
|
||||
'url': base + '/web/image/website.s_banner_default_image.jpg',
|
||||
})
|
||||
IrAttachment.create({
|
||||
'public': True,
|
||||
'name': 's_default_image2.jpg',
|
||||
'type': 'url',
|
||||
'url': base + '/web/image/website.s_banner_default_image.jpg',
|
||||
})
|
||||
self.start_tour("/", "snippet_image_gallery", login='admin')
|
||||
@@ -34,6 +34,7 @@
|
||||
<script type="text/javascript" src="/website/static/tests/tours/website_style_edition.js"/>
|
||||
<script type="text/javascript" src="/website/static/tests/tours/snippet_empty_parent_autoremove.js"/>
|
||||
<script type="text/javascript" src="/website/static/tests/tours/snippet_countdown.js"/>
|
||||
<script type="text/javascript" src="/website/static/tests/tours/snippet_image_gallery.js"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in new issue
Block a user