mirror of
https://github.com/NaN-tic/trytond-patches.git
synced 2023-12-14 06:03:03 +01:00
Add patches for issue4781
This commit is contained in:
parent
7fe7b18813
commit
0cb25736e8
3 changed files with 406 additions and 0 deletions
315
issue13211002_190001.diff
Normal file
315
issue13211002_190001.diff
Normal file
|
@ -0,0 +1,315 @@
|
|||
diff -r 694d32df45f1 inventory.py
|
||||
--- a/trytond/trytond/modules/stock/inventory.py Wed Jun 17 12:08:14 2015 +0200
|
||||
+++ b/trytond/trytond/modules/stock/inventory.py Wed Jun 17 12:14:25 2015 +0200
|
||||
@@ -1,6 +1,6 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level
|
||||
#of this repository contains the full copyright notices and license terms.
|
||||
-from trytond.model import Workflow, ModelView, ModelSQL, fields
|
||||
+from trytond.model import Workflow, Model, ModelView, ModelSQL, fields
|
||||
from trytond.pyson import Not, Equal, Eval, Or, Bool
|
||||
from trytond import backend
|
||||
from trytond.transaction import Transaction
|
||||
@@ -166,7 +166,12 @@
|
||||
return new_inventories
|
||||
|
||||
@staticmethod
|
||||
- def complete_lines(inventories):
|
||||
+ def grouping():
|
||||
+ return ('product',)
|
||||
+
|
||||
+ @classmethod
|
||||
+ @ModelView.button
|
||||
+ def complete_lines(cls, inventories):
|
||||
'''
|
||||
Complete or update the inventories
|
||||
'''
|
||||
@@ -174,25 +179,21 @@
|
||||
Line = pool.get('stock.inventory.line')
|
||||
Product = pool.get('product.product')
|
||||
|
||||
+ grouping = cls.grouping()
|
||||
to_create = []
|
||||
for inventory in inventories:
|
||||
# Compute product quantities
|
||||
with Transaction().set_context(stock_date_end=inventory.date):
|
||||
- pbl = Product.products_by_location([inventory.location.id])
|
||||
+ pbl = Product.products_by_location(
|
||||
+ [inventory.location.id], grouping=grouping)
|
||||
|
||||
# Index some data
|
||||
- product2uom = {}
|
||||
product2type = {}
|
||||
product2consumable = {}
|
||||
for product in Product.browse([line[1] for line in pbl]):
|
||||
- product2uom[product.id] = product.default_uom.id
|
||||
product2type[product.id] = product.type
|
||||
product2consumable[product.id] = product.consumable
|
||||
|
||||
- product_qty = {}
|
||||
- for (location, product), quantity in pbl.iteritems():
|
||||
- product_qty[product] = (quantity, product2uom[product])
|
||||
-
|
||||
# Update existing lines
|
||||
for line in inventory.lines:
|
||||
if not (line.product.active and
|
||||
@@ -200,26 +201,28 @@
|
||||
and not line.product.consumable):
|
||||
Line.delete([line])
|
||||
continue
|
||||
- if line.product.id in product_qty:
|
||||
- quantity, uom_id = product_qty.pop(line.product.id)
|
||||
- elif line.product.id in product2uom:
|
||||
- quantity, uom_id = 0.0, product2uom[line.product.id]
|
||||
+
|
||||
+ key = (inventory.location.id,) + line.unique_key
|
||||
+ if key in pbl:
|
||||
+ quantity = pbl.pop(key)
|
||||
else:
|
||||
- quantity, uom_id = 0.0, line.product.default_uom.id
|
||||
- values = line.update_values4complete(quantity, uom_id)
|
||||
+ quantity = 0.0
|
||||
+ values = line.update_values4complete(quantity)
|
||||
if values:
|
||||
Line.write([line], values)
|
||||
|
||||
# Create lines if needed
|
||||
- for product_id in product_qty:
|
||||
+ for key, quantity in pbl.iteritems():
|
||||
+ product_id = key[grouping.index('product') + 1]
|
||||
if (product2type[product_id] != 'goods'
|
||||
or product2consumable[product_id]):
|
||||
continue
|
||||
- quantity, uom_id = product_qty[product_id]
|
||||
if not quantity:
|
||||
continue
|
||||
- values = Line.create_values4complete(product_id, inventory,
|
||||
- quantity, uom_id)
|
||||
+
|
||||
+ values = Line.create_values4complete(inventory, quantity)
|
||||
+ for i, fname in enumerate(grouping, 1):
|
||||
+ values[fname] = key[i]
|
||||
to_create.append(values)
|
||||
if to_create:
|
||||
Line.create(to_create)
|
||||
@@ -310,7 +313,13 @@
|
||||
|
||||
@property
|
||||
def unique_key(self):
|
||||
- return (self.product,)
|
||||
+ key = []
|
||||
+ for fname in self.inventory.grouping():
|
||||
+ value = getattr(self, fname)
|
||||
+ if isinstance(value, Model):
|
||||
+ value = value.id
|
||||
+ key.append(value)
|
||||
+ return tuple(key)
|
||||
|
||||
@classmethod
|
||||
def cancel_move(cls, lines):
|
||||
@@ -349,31 +358,27 @@
|
||||
origin=self,
|
||||
)
|
||||
|
||||
- def update_values4complete(self, quantity, uom_id):
|
||||
+ def update_values4complete(self, quantity):
|
||||
'''
|
||||
Return update values to complete inventory
|
||||
'''
|
||||
values = {}
|
||||
# if nothing changed, no update
|
||||
- if self.quantity == self.expected_quantity == quantity \
|
||||
- and self.uom.id == uom_id:
|
||||
+ if self.quantity == self.expected_quantity == quantity:
|
||||
return values
|
||||
values['expected_quantity'] = quantity
|
||||
- values['uom'] = uom_id
|
||||
# update also quantity field if not edited
|
||||
if self.quantity == self.expected_quantity:
|
||||
values['quantity'] = max(quantity, 0.0)
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
- def create_values4complete(cls, product_id, inventory, quantity, uom_id):
|
||||
+ def create_values4complete(cls, inventory, quantity):
|
||||
'''
|
||||
Return create values to complete inventory
|
||||
'''
|
||||
return {
|
||||
'inventory': inventory.id,
|
||||
- 'product': product_id,
|
||||
'expected_quantity': quantity,
|
||||
'quantity': max(quantity, 0.0),
|
||||
- 'uom': uom_id,
|
||||
}
|
||||
diff -r 694d32df45f1 tests/scenario_stock_inventory.rst
|
||||
--- a/trytond/trytond/modules/stock/tests/scenario_stock_inventory.rst Wed Jun 17 12:08:14 2015 +0200
|
||||
+++ b/trytond/trytond/modules/stock/tests/scenario_stock_inventory.rst Wed Jun 17 12:14:25 2015 +0200
|
||||
@@ -64,13 +64,11 @@
|
||||
>>> storage_loc, = Location.find([('code', '=', 'STO')])
|
||||
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
|
||||
|
||||
-Create product::
|
||||
+Create products::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
- >>> Product = Model.get('product.product')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
- >>> product = Product()
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'Product'
|
||||
>>> template.default_uom = unit
|
||||
@@ -79,8 +77,18 @@
|
||||
>>> template.cost_price = Decimal('80')
|
||||
>>> template.cost_price_method = 'average'
|
||||
>>> template.save()
|
||||
- >>> product.template = template
|
||||
- >>> product.save()
|
||||
+ >>> product, = template.products
|
||||
+
|
||||
+ >>> kg, = ProductUom.find([('name', '=', 'Kilogram')])
|
||||
+ >>> template2 = ProductTemplate()
|
||||
+ >>> template2.name = 'Product'
|
||||
+ >>> template2.default_uom = kg
|
||||
+ >>> template2.type = 'goods'
|
||||
+ >>> template2.list_price = Decimal('140')
|
||||
+ >>> template2.cost_price = Decimal('60')
|
||||
+ >>> template2.cost_price_method = 'average'
|
||||
+ >>> template2.save()
|
||||
+ >>> product2, = template2.products
|
||||
|
||||
Fill storage::
|
||||
|
||||
@@ -96,7 +104,19 @@
|
||||
>>> incoming_move.company = company
|
||||
>>> incoming_move.unit_price = Decimal('100')
|
||||
>>> incoming_move.currency = currency
|
||||
- >>> incoming_move.save()
|
||||
+ >>> incoming_move.click('do')
|
||||
+
|
||||
+ >>> incoming_move = StockMove()
|
||||
+ >>> incoming_move.product = product2
|
||||
+ >>> incoming_move.uom = kg
|
||||
+ >>> incoming_move.quantity = 2.5
|
||||
+ >>> incoming_move.from_location = supplier_loc
|
||||
+ >>> incoming_move.to_location = storage_loc
|
||||
+ >>> incoming_move.planned_date = today
|
||||
+ >>> incoming_move.effective_date = today
|
||||
+ >>> incoming_move.company = company
|
||||
+ >>> incoming_move.unit_price = Decimal('70')
|
||||
+ >>> incoming_move.currency = company.currency
|
||||
>>> incoming_move.click('do')
|
||||
|
||||
Create an inventory::
|
||||
@@ -106,28 +126,81 @@
|
||||
>>> inventory.location = storage_loc
|
||||
>>> inventory.save()
|
||||
>>> inventory.click('complete_lines')
|
||||
- >>> line, = inventory.lines
|
||||
- >>> line.expected_quantity == 1
|
||||
- True
|
||||
- >>> line.quantity = 2
|
||||
+ >>> line_by_product = {l.product.id: l for l in inventory.lines}
|
||||
+ >>> line_p1 = line_by_product[product.id]
|
||||
+ >>> line_p1.expected_quantity
|
||||
+ 1.0
|
||||
+ >>> line_p1.quantity = 3
|
||||
+ >>> line_p2 = line_by_product[product2.id]
|
||||
+ >>> line_p2.expected_quantity
|
||||
+ 2.5
|
||||
+ >>> line_p2.quantity
|
||||
+ 2.5
|
||||
>>> inventory.save()
|
||||
+
|
||||
+Fill storage with more quantities::
|
||||
+
|
||||
+ >>> incoming_move = StockMove()
|
||||
+ >>> incoming_move.product = product
|
||||
+ >>> incoming_move.uom = unit
|
||||
+ >>> incoming_move.quantity = 1
|
||||
+ >>> incoming_move.from_location = supplier_loc
|
||||
+ >>> incoming_move.to_location = storage_loc
|
||||
+ >>> incoming_move.planned_date = today
|
||||
+ >>> incoming_move.effective_date = today
|
||||
+ >>> incoming_move.company = company
|
||||
+ >>> incoming_move.unit_price = Decimal('100')
|
||||
+ >>> incoming_move.currency = company.currency
|
||||
+ >>> incoming_move.click('do')
|
||||
+
|
||||
+ >>> incoming_move = StockMove()
|
||||
+ >>> incoming_move.product = product2
|
||||
+ >>> incoming_move.uom = kg
|
||||
+ >>> incoming_move.quantity = 1.3
|
||||
+ >>> incoming_move.from_location = supplier_loc
|
||||
+ >>> incoming_move.to_location = storage_loc
|
||||
+ >>> incoming_move.planned_date = today
|
||||
+ >>> incoming_move.effective_date = today
|
||||
+ >>> incoming_move.company = company
|
||||
+ >>> incoming_move.unit_price = Decimal('70')
|
||||
+ >>> incoming_move.currency = company.currency
|
||||
+ >>> incoming_move.click('do')
|
||||
+
|
||||
+Update the inventory::
|
||||
+
|
||||
+ >>> inventory.click('complete_lines')
|
||||
+ >>> line_p1.reload()
|
||||
+ >>> line_p1.expected_quantity
|
||||
+ 2.0
|
||||
+ >>> line_p1.quantity
|
||||
+ 3.0
|
||||
+ >>> line_p2.reload()
|
||||
+ >>> line_p2.expected_quantity
|
||||
+ 3.8
|
||||
+ >>> line_p2.quantity
|
||||
+ 3.8
|
||||
+
|
||||
+Confirm the inventory::
|
||||
+
|
||||
>>> inventory.click('confirm')
|
||||
- >>> line.reload()
|
||||
- >>> move, = line.moves
|
||||
- >>> move.quantity == 1
|
||||
- True
|
||||
+ >>> line_p1.reload()
|
||||
+ >>> move, = line_p1.moves
|
||||
+ >>> move.quantity
|
||||
+ 1.0
|
||||
>>> move.from_location == inventory.lost_found
|
||||
True
|
||||
>>> move.to_location == inventory.location
|
||||
True
|
||||
+ >>> line_p2.reload()
|
||||
+ >>> len(line_p2.moves)
|
||||
+ 0
|
||||
|
||||
Empty storage::
|
||||
|
||||
- >>> StockMove = Model.get('stock.move')
|
||||
>>> outgoing_move = StockMove()
|
||||
>>> outgoing_move.product = product
|
||||
>>> outgoing_move.uom = unit
|
||||
- >>> outgoing_move.quantity = 2
|
||||
+ >>> outgoing_move.quantity = 3
|
||||
>>> outgoing_move.from_location = storage_loc
|
||||
>>> outgoing_move.to_location = customer_loc
|
||||
>>> outgoing_move.planned_date = today
|
||||
@@ -137,6 +210,19 @@
|
||||
>>> outgoing_move.currency = currency
|
||||
>>> outgoing_move.click('do')
|
||||
|
||||
+ >>> outgoing_move = StockMove()
|
||||
+ >>> outgoing_move.product = product2
|
||||
+ >>> outgoing_move.uom = kg
|
||||
+ >>> outgoing_move.quantity = 3.8
|
||||
+ >>> outgoing_move.from_location = storage_loc
|
||||
+ >>> outgoing_move.to_location = customer_loc
|
||||
+ >>> outgoing_move.planned_date = today
|
||||
+ >>> outgoing_move.effective_date = today
|
||||
+ >>> outgoing_move.company = company
|
||||
+ >>> outgoing_move.unit_price = Decimal('140')
|
||||
+ >>> outgoing_move.currency = company.currency
|
||||
+ >>> outgoing_move.click('do')
|
||||
+
|
||||
Create an inventory that should be empty after completion::
|
||||
|
||||
>>> Inventory = Model.get('stock.inventory')
|
89
issue17281002_20001.diff
Normal file
89
issue17281002_20001.diff
Normal file
|
@ -0,0 +1,89 @@
|
|||
diff -r e9a7f9fd73aa stock.py
|
||||
--- a/trytond/trytond/modules/stock_lot/stock.py Wed Jun 17 11:46:34 2015 +0200
|
||||
+++ b/trytond/trytond/modules/stock_lot/stock.py Wed Jun 17 11:47:43 2015 +0200
|
||||
@@ -1,7 +1,6 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
import datetime
|
||||
-from collections import defaultdict
|
||||
|
||||
from trytond.model import ModelView, ModelSQL, Workflow, fields
|
||||
from trytond.pyson import PYSONEncoder, Eval
|
||||
@@ -200,64 +199,8 @@
|
||||
__name__ = 'stock.inventory'
|
||||
|
||||
@classmethod
|
||||
- def complete_lines(cls, inventories):
|
||||
- pool = Pool()
|
||||
- Product = pool.get('product.product')
|
||||
- Line = pool.get('stock.inventory.line')
|
||||
-
|
||||
- super(Inventory, cls).complete_lines(inventories)
|
||||
-
|
||||
- # Create and/or update lines with product that will require lot for
|
||||
- # their moves.
|
||||
- to_create = []
|
||||
- for inventory in inventories:
|
||||
- product2lines = defaultdict(list)
|
||||
- for line in inventory.lines:
|
||||
- if (line.product.lot_is_required(inventory.location,
|
||||
- inventory.lost_found)
|
||||
- or line.product.lot_is_required(inventory.lost_found,
|
||||
- inventory.location)):
|
||||
- product2lines[line.product.id].append(line)
|
||||
- if product2lines:
|
||||
- with Transaction().set_context(stock_date_end=inventory.date):
|
||||
- pbl = Product.products_by_location([inventory.location.id],
|
||||
- product_ids=product2lines.keys(),
|
||||
- grouping=('product', 'lot'))
|
||||
- product_qty = defaultdict(dict)
|
||||
- for (location_id, product_id, lot_id), quantity \
|
||||
- in pbl.iteritems():
|
||||
- product_qty[product_id][lot_id] = quantity
|
||||
-
|
||||
- products = Product.browse(product_qty.keys())
|
||||
- product2uom = dict((p.id, p.default_uom.id) for p in products)
|
||||
-
|
||||
- for product_id, lines in product2lines.iteritems():
|
||||
- quantities = product_qty[product_id]
|
||||
- uom_id = product2uom[product_id]
|
||||
- for line in lines:
|
||||
- lot_id = line.lot.id if line.lot else None
|
||||
- if lot_id in quantities:
|
||||
- quantity = quantities.pop(lot_id)
|
||||
- elif lot_id is None and quantities:
|
||||
- lot_id = quantities.keys()[0]
|
||||
- quantity = quantities.pop(lot_id)
|
||||
- else:
|
||||
- lot_id = None
|
||||
- quantity = 0.0
|
||||
-
|
||||
- values = line.update_values4complete(quantity, uom_id)
|
||||
- if (values or lot_id != (line.lot.id
|
||||
- if line.lot else None)):
|
||||
- values['lot'] = lot_id
|
||||
- Line.write([line], values)
|
||||
- if quantities:
|
||||
- for lot_id, quantity in quantities.iteritems():
|
||||
- values = Line.create_values4complete(product_id,
|
||||
- inventory, quantity, uom_id)
|
||||
- values['lot'] = lot_id
|
||||
- to_create.append(values)
|
||||
- if to_create:
|
||||
- Line.create(to_create)
|
||||
+ def grouping(cls):
|
||||
+ return super(Inventory, cls).grouping() + ('lot', )
|
||||
|
||||
|
||||
class InventoryLine:
|
||||
@@ -279,10 +222,6 @@
|
||||
rec_name += ' - %s' % self.lot.rec_name
|
||||
return rec_name
|
||||
|
||||
- @property
|
||||
- def unique_key(self):
|
||||
- return super(InventoryLine, self).unique_key + (self.lot,)
|
||||
-
|
||||
def get_move(self):
|
||||
move = super(InventoryLine, self).get_move()
|
||||
if move:
|
2
series
2
series
|
@ -54,3 +54,5 @@ issue9801002_40001.diff
|
|||
issue19281002_1.diff
|
||||
issue13181002_1.diff
|
||||
issue12191002_1.diff
|
||||
#issue13211002_190001.diff
|
||||
#issue17281002_20001.diff
|
||||
|
|
Loading…
Reference in a new issue