[MERGE] forward port branch 8.0 up to 89eca96476

This commit is contained in:
Christophe Simonis
2017-06-15 17:26:15 +02:00
7 changed files with 206 additions and 25 deletions
+2 -5
View File
@@ -23,13 +23,10 @@
from lxml import etree
import os
import base64
try:
import cPickle as pickle
except ImportError:
import pickle
import random
import datetime
from openerp.osv import fields, osv
from openerp.tools import pickle
from openerp.tools.translate import _
from openerp.tools.safe_eval import safe_eval as eval
@@ -462,7 +459,7 @@ class ir_model_fields_anonymize_wizard(osv.osv_memory):
# save pickle:
fn = open(abs_filepath, 'w')
pickle.dump(data, fn, pickle.HIGHEST_PROTOCOL)
pickle.dump(data, fn, -1)
# update the anonymization fields:
values = {
+3
View File
@@ -10,6 +10,9 @@ from openerp.addons.auth_signup.res_users import SignupError
from openerp.osv import osv, fields
from openerp import SUPERUSER_ID
from openerp.addons.base.res import res_users
res_users.USER_PRIVATE_FIELDS.append('oauth_access_token')
_logger = logging.getLogger(__name__)
class res_users(osv.Model):
@@ -3,4 +3,11 @@ xset s off
xset -dpms
export HOME=/tmp
/usr/bin/firefox http://localhost:8069/point_of_sale/display &
DISPLAY_URL='http://localhost:8069/point_of_sale/display'
# wait until odoo is ready, otherwise you will have to plug a
# keyboard into the posbox just to refresh firefox
until wget -q "${DISPLAY_URL}" -O /dev/null ; do sleep 1 ; done
/usr/bin/firefox "${DISPLAY_URL}" &
+47 -18
View File
@@ -3039,35 +3039,64 @@ class stock_inventory_line(osv.osv):
res['value']['product_qty'] = th_qty
return res
def _resolve_inventory_line(self, cr, uid, inventory_line, context=None):
stock_move_obj = self.pool.get('stock.move')
quant_obj = self.pool.get('stock.quant')
diff = inventory_line.theoretical_qty - inventory_line.product_qty
if not diff:
return
#each theorical_lines where difference between theoretical and checked quantities is not 0 is a line for which we need to create a stock move
vals = {
# Do not forward port in 10.0 and beyond
def _get_move_values(self, cr, uid, inventory_line, qty, location_id, location_dest_id):
return {
'name': _('INV:') + (inventory_line.inventory_id.name or ''),
'product_id': inventory_line.product_id.id,
'product_uom': inventory_line.product_uom_id.id,
'product_uom_qty': qty,
'date': inventory_line.inventory_id.date,
'company_id': inventory_line.inventory_id.company_id.id,
'inventory_id': inventory_line.inventory_id.id,
'state': 'confirmed',
'restrict_lot_id': inventory_line.prod_lot_id.id,
'restrict_partner_id': inventory_line.partner_id.id,
}
'location_id': location_id,
'location_dest_id': location_dest_id,
}
def _fixup_negative_quants(self, cr, uid, inventory_line):
""" This will handle the irreconciable quants created by a force availability followed by a
return. When generating the moves of an inventory line, we look for quants of this line's
product created to compensate a force availability. If there are some and if the quant
which it is propagated from is still in the same location, we move it to the inventory
adjustment location before getting it back. Getting the quantity from the inventory
location will allow the negative quant to be compensated.
"""
quant_obj = self.pool.get('stock.quant')
stock_move_obj = self.pool.get('stock.move')
quant_ids = self._get_quants(cr, uid, inventory_line)
for quant in quant_obj.browse(cr, uid, quant_ids).filtered(lambda q: q.propagated_from_id.location_id.id == inventory_line.location_id.id):
# send the quantity to the inventory adjustment location
move_out_vals = self._get_move_values(cr, uid, inventory_line, quant.qty, inventory_line.location_id.id, inventory_line.product_id.property_stock_inventory.id)
move_out = stock_move_obj.create(cr, uid, move_out_vals)
move_out = stock_move_obj.browse(cr, uid, [move_out])
quant_obj.quants_reserve(cr, uid, [(quant, quant.qty)], move_out)
move_out.action_done()
# get back the quantity from the inventory adjustment location
move_in_vals = self._get_move_values(cr, uid, inventory_line, quant.qty, inventory_line.product_id.property_stock_inventory.id, inventory_line.location_id.id)
move_in = stock_move_obj.create(cr, uid, move_in_vals)
move_in = stock_move_obj.browse(cr, uid, [move_in])
move_in.action_done()
def _resolve_inventory_line(self, cr, uid, inventory_line, context=None):
stock_move_obj = self.pool.get('stock.move')
quant_obj = self.pool.get('stock.quant')
self._fixup_negative_quants(cr, uid, inventory_line)
if float_compare(inventory_line.theoretical_qty, inventory_line.product_qty, precision_rounding=inventory_line.product_id.uom_id.rounding) == 0:
return False
diff = inventory_line.theoretical_qty - inventory_line.product_qty
#each theorical_lines where difference between theoretical and checked quantities is not 0 is a line for which we need to create a stock move
inventory_location_id = inventory_line.product_id.property_stock_inventory.id
if diff < 0:
#found more than expected
vals['location_id'] = inventory_location_id
vals['location_dest_id'] = inventory_line.location_id.id
vals['product_uom_qty'] = -diff
if diff < 0: # found more than expected
vals = self._get_move_values(cr, uid, inventory_line, abs(diff), inventory_location_id, inventory_line.location_id.id)
else:
#found less than expected
vals['location_id'] = inventory_line.location_id.id
vals['location_dest_id'] = inventory_location_id
vals['product_uom_qty'] = diff
vals = self._get_move_values(cr, uid, inventory_line, abs(diff), inventory_line.location_id.id, inventory_location_id)
move_id = stock_move_obj.create(cr, uid, vals, context=context)
move = stock_move_obj.browse(cr, uid, move_id, context=context)
if diff > 0:
+131 -1
View File
@@ -1269,4 +1269,134 @@ class TestStockFlow(TestStockCommon):
self.assertEqual(len(neg_quants), 0, 'There are negative quants!')
# We should also make sure that when matching stock moves with pack operations, it takes the correct
self.assertEqual(len(picking_out.move_lines[0].linked_move_operation_ids), 2, 'We should only have 2 links beween the move and the 2 operations')
self.assertEqual(len(picking_out.move_lines[0].quant_ids), 2, 'We should have exactly 2 quants in the end')
self.assertEqual(len(picking_out.move_lines[0].quant_ids), 2, 'We should have exactly 2 quants in the end')
# Do not forward port in 10.0 and beyond
def test_inventory_adjustment_and_negative_quants_1(self):
"""Make sure negative quants from returns get wiped out with an inventory adjustment"""
productA = self.env['product.product'].create({'name': 'Product A', 'type': 'product'})
stock_location = self.env.ref('stock.stock_location_stock')
customer_location = self.env.ref('stock.stock_location_customers')
location_loss = self.env.ref('stock.location_inventory')
# Create a picking out and force availability
picking_out = self.env['stock.picking'].create({
'partner_id': self.env.ref('base.res_partner_2').id,
'picking_type_id': self.env.ref('stock.picking_type_out').id,
'location_id': stock_location.id,
'location_dest_id': customer_location.id,
})
self.env['stock.move'].create({
'name': productA.name,
'product_id': productA.id,
'product_uom_qty': 1,
'product_uom': productA.uom_id.id,
'picking_id': picking_out.id,
'location_id': stock_location.id,
'location_dest_id': customer_location.id,
})
picking_out.action_confirm()
picking_out.force_assign()
picking_out.do_transfer()
# Create return picking for all goods
default_data = self.env['stock.return.picking']\
.with_context(active_ids=picking_out.ids, active_id=picking_out.ids[0])\
.default_get([
'move_dest_exists',
'product_return_moves'
])
list_return_moves = default_data['product_return_moves']
default_data['product_return_moves'] = [(0, 0, return_move) for return_move in list_return_moves]
return_wiz = self.env['stock.return.picking']\
.with_context(active_ids=picking_out.ids, active_id=picking_out.ids[0])\
.create(default_data)
res = return_wiz._create_returns()[0]
return_pick = self.env['stock.picking'].browse(res)
return_pick.action_assign()
return_pick.do_transfer()
# Make an inventory adjustment to set the quantity to 0
inventory = self.env['stock.inventory'].create({
'name': 'Starting for product_1',
'filter': 'product',
'location_id': stock_location.id,
'product_id': productA.id,
})
inventory.prepare_inventory()
self.assertEqual(len(inventory.line_ids), 1, "Wrong inventory lines generated.")
self.assertEqual(inventory.line_ids.theoretical_qty, 0, "Theoretical quantity should be zero.")
inventory.action_done()
# The inventory adjustment should have created two moves
self.assertEqual(len(inventory.move_ids), 2)
quantity = inventory.move_ids.mapped('product_qty')
self.assertEqual(quantity, [1, 1], "Moves created with wrong quantity.")
location_ids = inventory.move_ids.mapped('location_id').ids
self.assertEqual(set(location_ids), {stock_location.id, location_loss.id})
# There should be no quant in the stock location
quants = self.env['stock.quant'].search([('product_id', '=', productA.id), ('location_id', '=', stock_location.id)])
self.assertEqual(len(quants), 0)
# There should be one quant in the inventory loss location
quant = self.env['stock.quant'].search([('product_id', '=', productA.id), ('location_id', '=', location_loss.id)])
self.assertEqual(len(quant), 1)
self.assertEqual(quant.qty, 1)
def test_inventory_adjustment_and_negative_quants_2(self):
"""Make sure negative quants get wiped out with an inventory adjustment"""
productA = self.env['product.product'].create({'name': 'Product A', 'type': 'product'})
stock_location = self.env.ref('stock.stock_location_stock')
customer_location = self.env.ref('stock.stock_location_customers')
location_loss = self.env.ref('stock.location_inventory')
# Create a picking out and force availability
picking_out = self.env['stock.picking'].create({
'partner_id': self.env.ref('base.res_partner_2').id,
'picking_type_id': self.env.ref('stock.picking_type_out').id,
'location_id': stock_location.id,
'location_dest_id': customer_location.id,
})
self.env['stock.move'].create({
'name': productA.name,
'product_id': productA.id,
'product_uom_qty': 1,
'product_uom': productA.uom_id.id,
'picking_id': picking_out.id,
'location_id': stock_location.id,
'location_dest_id': customer_location.id,
})
picking_out.action_confirm()
picking_out.force_assign()
picking_out.do_transfer()
# Make an inventory adjustment to set the quantity to 0
inventory = self.env['stock.inventory'].create({
'name': 'Starting for product_1',
'filter': 'product',
'location_id': stock_location.id,
'product_id': productA.id,
})
inventory.prepare_inventory()
self.assertEqual(len(inventory.line_ids), 1, "Wrong inventory lines generated.")
self.assertEqual(inventory.line_ids.theoretical_qty, -1, "Theoretical quantity should be -1.")
inventory.line_ids.product_qty = 0 # Put the quantity back to 0
inventory.action_done()
# The inventory adjustment should have created one
self.assertEqual(len(inventory.move_ids), 1)
quantity = inventory.move_ids.mapped('product_qty')
self.assertEqual(quantity, [1], "Moves created with wrong quantity.")
location_ids = inventory.move_ids.mapped('location_id').ids
self.assertEqual(set(location_ids), {location_loss.id})
# There should be no quant in the stock location
quants = self.env['stock.quant'].search([('product_id', '=', productA.id), ('location_id', '=', stock_location.id)])
self.assertEqual(len(quants), 0)
# There should be no quant in the inventory loss location
quant = self.env['stock.quant'].search([('product_id', '=', productA.id), ('location_id', '=', location_loss.id)])
self.assertEqual(len(quant), 0)
+2
View File
@@ -162,6 +162,8 @@ def redirect_with_hash(url, code=303):
# See extensive test page at http://greenbytes.de/tech/tc/httpredirects/
if request.httprequest.user_agent.browser in ('firefox',):
return werkzeug.utils.redirect(url, code)
if urlparse.urlparse(url, scheme='http').scheme not in ('http', 'https'):
url = 'http://' + url
url = url.replace("'", "%27").replace("<", "%3C")
return "<html><head><script>window.location = '%s' + location.hash;</script></head></html>" % url
+13
View File
@@ -63,6 +63,19 @@ psycopg2.extensions.register_type(psycopg2.extensions.new_type((700, 701, 1700,)
import tools
from tools import parse_version as pv
if pv(psycopg2.__version__) < pv('2.7'):
from psycopg2._psycopg import QuotedString
def adapt_string(adapted):
"""Python implementation of psycopg/psycopg2#459 from v2.7"""
if '\x00' in adapted:
raise ValueError("A string literal cannot contain NUL (0x00) characters.")
return QuotedString(adapted)
psycopg2.extensions.register_adapter(str, adapt_string)
psycopg2.extensions.register_adapter(unicode, adapt_string)
from tools.func import frame_codeinfo
from datetime import datetime as mdt
from datetime import timedelta