mirror of
https://github.com/NaN-tic/trytond-patches.git
synced 2023-12-14 06:03:03 +01:00
315 lines
11 KiB
Diff
315 lines
11 KiB
Diff
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')
|