Files
tryton/modules/stock/inventory.py
T

664 lines
23 KiB
Python
Raw Normal View History

2014-12-02 12:56:04 +01:00
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
2021-10-30 02:27:19 +02:00
from collections import defaultdict
2023-08-03 15:53:18 +02:00
from sql.functions import CharLength
2018-12-29 14:20:29 +01:00
from trytond.i18n import gettext
from trytond.model import (
Check, Index, Model, ModelSQL, ModelView, Workflow, fields)
2018-12-29 14:20:29 +01:00
from trytond.model.exceptions import AccessError
2021-12-11 17:58:44 +01:00
from trytond.pool import Pool
from trytond.pyson import Bool, Eval, If
2023-06-09 09:15:31 +02:00
from trytond.tools import grouped_slice, is_full_text, lstrip_wildcard
2010-08-19 22:22:44 +02:00
from trytond.transaction import Transaction
2021-12-11 17:58:44 +01:00
from trytond.wizard import Button, StateTransition, StateView, Wizard
2008-05-06 11:24:28 +02:00
2020-10-12 20:31:27 +02:00
from .exceptions import (
2021-12-11 17:58:44 +01:00
InventoryCountWarning, InventoryFutureWarning, InventoryValidationError)
2018-12-29 14:20:29 +01:00
2008-05-08 17:33:44 +02:00
2012-03-25 00:18:19 +01:00
class Inventory(Workflow, ModelSQL, ModelView):
2008-05-06 11:24:28 +02:00
'Stock Inventory'
2012-09-24 22:18:17 +02:00
__name__ = 'stock.inventory'
2016-03-22 17:00:58 +01:00
_rec_name = 'number'
_states = {
'readonly': Eval('state') != 'draft',
}
2018-12-04 12:25:45 +01:00
number = fields.Char('Number', readonly=True,
help="The main identifier for the inventory.")
2008-05-06 11:24:28 +02:00
location = fields.Many2One(
'stock.location', 'Location', required=True,
2008-11-11 23:33:38 +01:00
domain=[('type', '=', 'storage')], states={
2015-06-28 23:45:31 +02:00
'readonly': (Eval('state') != 'draft') | Eval('lines', [0]),
2011-08-04 16:57:42 +02:00
},
2018-12-04 12:25:45 +01:00
help="The location inventoried.")
date = fields.Date('Date', required=True, states={
2015-06-28 23:45:31 +02:00
'readonly': (Eval('state') != 'draft') | Eval('lines', [0]),
2011-08-04 16:57:42 +02:00
},
2018-12-04 12:25:45 +01:00
help="The date of the stock count.")
2008-05-06 11:24:28 +02:00
lines = fields.One2Many(
'stock.inventory.line', 'inventory', 'Lines',
states={
'readonly': (_states['readonly'] | ~Eval('location')
| ~Eval('date')),
})
empty_quantity = fields.Selection([
(None, ""),
('keep', "Keep"),
('empty', "Empty"),
], "Empty Quantity", states=_states,
2020-10-03 23:47:19 +02:00
help="How lines without a quantity are handled.")
2010-01-20 11:04:28 +01:00
company = fields.Many2One('company.company', 'Company', required=True,
states={
2015-06-28 23:45:31 +02:00
'readonly': (Eval('state') != 'draft') | Eval('lines', [0]),
2011-08-04 16:57:42 +02:00
},
2018-12-04 12:25:45 +01:00
help="The company the inventory is associated with.")
state = fields.Selection([
('draft', "Draft"),
('done', "Done"),
('cancelled', "Cancelled"),
], "State", readonly=True, sort=False,
2018-12-04 12:25:45 +01:00
help="The current state of the inventory.")
2008-05-06 11:24:28 +02:00
del _states
2012-09-24 22:18:17 +02:00
@classmethod
def __setup__(cls):
cls.number.search_unaccented = False
2012-09-24 22:18:17 +02:00
super(Inventory, cls).__setup__()
t = cls.__table__()
cls._sql_indexes.add(
Index(t, (t.state, Index.Equality()), where=t.state == 'draft'))
2012-09-24 22:18:17 +02:00
cls._order.insert(0, ('date', 'DESC'))
cls._transitions |= set((
2012-03-25 00:18:19 +01:00
('draft', 'done'),
('draft', 'cancelled'),
2012-03-25 00:18:19 +01:00
))
2012-09-24 22:18:17 +02:00
cls._buttons.update({
2012-03-25 00:18:19 +01:00
'confirm': {
'invisible': Eval('state').in_(['done', 'cancelled']),
2018-02-19 11:41:53 +01:00
'depends': ['state'],
2012-03-25 00:18:19 +01:00
},
'cancel': {
'invisible': Eval('state').in_(['cancelled', 'done']),
2018-02-19 11:41:53 +01:00
'depends': ['state'],
2012-03-25 00:18:19 +01:00
},
'complete_lines': {
'readonly': Eval('state') != 'draft',
2018-02-19 11:41:53 +01:00
'depends': ['state'],
2012-03-25 00:18:19 +01:00
},
2022-05-07 18:48:45 +02:00
'do_count': {
2018-07-27 17:07:31 +02:00
'readonly': Eval('state') != 'draft',
'depends': ['state'],
},
2012-03-25 00:18:19 +01:00
})
2008-05-07 14:56:54 +02:00
2012-09-24 22:18:17 +02:00
@classmethod
def __register__(cls, module_name):
super(Inventory, cls).__register__(module_name)
cursor = Transaction().connection.cursor()
2018-07-24 22:01:08 +02:00
table = cls.__table_handler__(module_name)
sql_table = cls.__table__()
2018-07-24 22:01:08 +02:00
# Migration from 5.4: remove lost_found
table.not_null_action('lost_found', 'remove')
# Migration from 5.6: rename state cancel to cancelled
cursor.execute(*sql_table.update(
[sql_table.state], ['cancelled'],
where=sql_table.state == 'cancel'))
2023-08-03 15:53:18 +02:00
@classmethod
def order_number(cls, tables):
table, _ = tables[None]
return [CharLength(table.number), table.number]
2012-09-24 22:18:17 +02:00
@staticmethod
def default_state():
2008-11-04 23:38:48 +01:00
return 'draft'
2008-05-06 11:24:28 +02:00
2012-09-24 22:18:17 +02:00
@staticmethod
def default_date():
Date = Pool().get('ir.date')
return Date.today()
2008-05-07 14:56:54 +02:00
2012-09-24 22:18:17 +02:00
@staticmethod
def default_company():
2012-03-27 10:44:04 +02:00
return Transaction().context.get('company')
2008-05-22 01:58:47 +02:00
2023-06-09 09:15:31 +02:00
def get_rec_name(self, name):
pool = Pool()
Lang = pool.get('ir.lang')
lang = Lang.get()
date = lang.strftime(self.date)
return f"[{self.number}] {self.location.rec_name} @ {date}"
@classmethod
def search_rec_name(cls, name, clause):
_, operator, operand, *extra = clause
if operator.startswith('!') or operator.startswith('not '):
bool_op = 'AND'
else:
bool_op = 'OR'
number_value = operand
if operator.endswith('like') and is_full_text(operand):
number_value = lstrip_wildcard(operand)
return [bool_op,
('number', operator, number_value, *extra),
('location.rec_name', operator, operand, *extra),
]
2019-09-20 11:47:15 +02:00
@classmethod
def view_attributes(cls):
2021-04-09 10:52:03 +02:00
return super().view_attributes() + [
('/tree', 'visual', If(Eval('state') == 'cancelled', 'muted', '')),
2019-09-20 11:47:15 +02:00
]
2012-09-24 22:18:17 +02:00
@classmethod
def delete(cls, inventories):
# Cancel before delete
2012-09-24 22:18:17 +02:00
cls.cancel(inventories)
for inventory in inventories:
if inventory.state != 'cancelled':
2018-12-29 14:20:29 +01:00
raise AccessError(
gettext('stock.msg_inventory_delete_cancel',
inventory=inventory.rec_name))
2012-09-24 22:18:17 +02:00
super(Inventory, cls).delete(inventories)
2012-09-24 22:18:17 +02:00
@classmethod
2012-03-25 00:18:19 +01:00
@ModelView.button
@Workflow.transition('done')
2014-12-09 23:33:17 +01:00
def confirm(cls, inventories):
2020-10-12 20:31:27 +02:00
pool = Pool()
Move = pool.get('stock.move')
Date = pool.get('ir.date')
Warning = pool.get('res.user.warning')
today_cache = {}
def in_future(inventory):
if inventory.company not in today_cache:
with Transaction().set_context(company=inventory.company.id):
today_cache[inventory.company] = Date.today()
today = today_cache[inventory.company]
if inventory.date > today:
return inventory
future_inventories = sorted(filter(in_future, inventories))
if future_inventories:
names = ', '.join(i.rec_name for i in future_inventories[:5])
if len(future_inventories) > 5:
names + '...'
warning_name = Warning.format('date_future', future_inventories)
2020-10-12 20:31:27 +02:00
if Warning.check(warning_name):
raise InventoryFutureWarning(warning_name,
gettext('stock.msg_inventory_date_in_the_future',
inventories=names))
2013-11-28 23:21:45 +01:00
moves = []
2012-09-24 22:18:17 +02:00
for inventory in inventories:
2013-06-21 23:24:17 +02:00
keys = set()
2012-03-25 00:18:19 +01:00
for line in inventory.lines:
2013-06-21 23:24:17 +02:00
key = line.unique_key
if key in keys:
2018-12-29 14:20:29 +01:00
raise InventoryValidationError(
gettext('stock.msg_inventory_line_unique',
line=line.rec_name,
inventory=inventory.rec_name))
2013-06-21 23:24:17 +02:00
keys.add(key)
2013-11-28 23:21:45 +01:00
move = line.get_move()
if move:
moves.append(move)
if moves:
2014-11-01 11:54:45 +01:00
Move.save(moves)
2020-10-12 20:31:27 +02:00
# Skip MoveFutureWarning as it is newly created moves
with Transaction().set_user(0):
Move.do(moves)
2008-10-08 12:19:58 +02:00
2012-09-24 22:18:17 +02:00
@classmethod
2012-03-25 00:18:19 +01:00
@ModelView.button
@Workflow.transition('cancelled')
2014-12-09 23:33:17 +01:00
def cancel(cls, inventories):
2012-09-24 22:18:17 +02:00
Line = Pool().get("stock.inventory.line")
Line.cancel_move([l for i in inventories for l in i.lines])
2008-05-07 14:56:54 +02:00
@classmethod
2016-03-22 17:00:58 +01:00
def create(cls, vlist):
pool = Pool()
Configuration = pool.get('stock.configuration')
config = Configuration(1)
vlist = [x.copy() for x in vlist]
2021-04-03 14:27:10 +02:00
default_company = cls.default_company()
2016-03-22 17:00:58 +01:00
for values in vlist:
if values.get('number') is None:
2021-04-03 14:27:10 +02:00
values['number'] = config.get_multivalue(
'inventory_sequence',
company=values.get('company', default_company)).get()
2016-03-22 17:00:58 +01:00
inventories = super(Inventory, cls).create(vlist)
cls.complete_lines(inventories, fill=False)
return inventories
@classmethod
def write(cls, *args):
super().write(*args)
inventories = cls.browse(set(sum(args[::2], [])))
cls.complete_lines(inventories, fill=False)
2012-09-24 22:18:17 +02:00
@classmethod
def copy(cls, inventories, default=None):
pool = Pool()
Date = pool.get('ir.date')
2008-12-05 13:54:13 +01:00
2008-11-04 23:38:48 +01:00
if default is None:
default = {}
2018-09-01 01:02:04 +02:00
else:
default = default.copy()
default.setdefault('date', Date.today())
default.setdefault('lines.moves', None)
default.setdefault('number', None)
2008-12-05 13:54:13 +01:00
2018-09-01 01:02:04 +02:00
new_inventories = super().copy(inventories, default=default)
cls.complete_lines(new_inventories, fill=False)
2012-09-24 22:18:17 +02:00
return new_inventories
2008-12-05 13:54:13 +01:00
2012-12-25 11:28:05 +01:00
@staticmethod
def grouping():
return ('product',)
@classmethod
@ModelView.button
def complete_lines(cls, inventories, fill=True):
'''
Complete or update the inventories
'''
2011-07-03 13:19:14 +02:00
pool = Pool()
2012-09-24 22:18:17 +02:00
Line = pool.get('stock.inventory.line')
Product = pool.get('product.product')
2008-05-08 14:16:28 +02:00
grouping = cls.grouping()
2016-06-23 09:40:00 +02:00
to_create, to_write = [], []
2008-05-08 14:16:28 +02:00
for inventory in inventories:
# Once done computation is wrong because include created moves
if inventory.state == 'done':
continue
# Compute product quantities
2023-03-09 13:37:12 +01:00
with Transaction().set_context(
company=inventory.company.id,
stock_date_end=inventory.date):
2021-10-30 02:27:19 +02:00
if fill:
pbl = Product.products_by_location(
[inventory.location.id],
grouping=grouping)
else:
product_ids = [l.product.id for l in inventory.lines]
pbl = defaultdict(int)
for product_ids in grouped_slice(product_ids):
pbl.update(Product.products_by_location(
[inventory.location.id],
grouping=grouping,
grouping_filter=(list(product_ids),)))
# Index some data
product2type = {}
product2consumable = {}
2019-10-25 10:08:35 +02:00
for product in Product.browse({line[1] for line in pbl}):
product2type[product.id] = product.type
product2consumable[product.id] = product.consumable
# Update existing lines
for line in inventory.lines:
2019-09-19 14:04:54 +02:00
if line.product.type != 'goods':
2012-09-24 22:18:17 +02:00
Line.delete([line])
2009-09-23 16:42:40 +02:00
continue
key = (inventory.location.id,) + line.unique_key
if key in pbl:
quantity = pbl.pop(key)
else:
quantity = 0.0
values = line.update_values4complete(quantity)
if values:
2016-06-23 09:40:00 +02:00
to_write.extend(([line], values))
if not fill:
continue
# Create lines if needed
2018-06-26 19:11:55 +02:00
for key, quantity in pbl.items():
product_id = key[grouping.index('product') + 1]
if (product2type[product_id] != 'goods'
2013-01-01 22:23:32 +01:00
or product2consumable[product_id]):
continue
if not quantity:
continue
values = Line.create_values4complete(inventory, quantity)
for i, fname in enumerate(grouping, 1):
values[fname] = key[i]
2013-01-06 22:18:13 +01:00
to_create.append(values)
2013-06-21 23:24:17 +02:00
if to_create:
Line.create(to_create)
2016-06-23 09:40:00 +02:00
if to_write:
Line.write(*to_write)
2008-11-04 23:38:48 +01:00
2018-07-27 17:07:31 +02:00
@classmethod
@ModelView.button_action('stock.wizard_inventory_count')
2022-05-07 18:48:45 +02:00
def do_count(cls, inventories):
2018-07-27 17:07:31 +02:00
cls.complete_lines(inventories)
2008-11-04 23:38:48 +01:00
2009-02-28 17:04:50 +01:00
class InventoryLine(ModelSQL, ModelView):
2008-11-04 23:38:48 +01:00
'Stock Inventory Line'
2012-09-24 22:18:17 +02:00
__name__ = 'stock.inventory.line'
2016-06-30 18:54:34 +02:00
_states = {
'readonly': Eval('inventory_state') != 'draft',
}
2008-11-04 23:38:48 +01:00
product = fields.Many2One('product.product', 'Product', required=True,
domain=[
('type', '=', 'goods'),
], states=_states)
2023-08-22 13:18:33 +02:00
unit = fields.Function(fields.Many2One(
'product.uom', "Unit",
help="The unit in which the quantity is specified."),
'get_unit')
2021-08-30 00:27:13 +02:00
expected_quantity = fields.Float(
2023-08-22 13:18:33 +02:00
"Expected Quantity", digits='unit', required=True, readonly=True,
states={
'invisible': Eval('id', -1) < 0,
},
2018-12-04 12:25:45 +01:00
help="The quantity the system calculated should be in the location.")
2021-08-30 00:27:13 +02:00
quantity = fields.Float(
2023-08-22 13:18:33 +02:00
"Actual Quantity", digits='unit', states=_states,
2018-12-04 12:25:45 +01:00
help="The actual quantity found in the location.")
2013-11-28 23:21:45 +01:00
moves = fields.One2Many('stock.move', 'origin', 'Moves', readonly=True)
2009-01-08 20:04:31 +01:00
inventory = fields.Many2One('stock.inventory', 'Inventory', required=True,
2016-06-30 18:54:34 +02:00
ondelete='CASCADE',
states={
'readonly': _states['readonly'] & Bool(Eval('inventory')),
},
2018-12-04 12:25:45 +01:00
help="The inventory the line belongs to.")
2020-10-03 23:47:19 +02:00
inventory_location = fields.Function(
fields.Many2One('stock.location', "Location"),
'on_change_with_inventory_location',
searcher='search_inventory_location')
inventory_date = fields.Function(
fields.Date("Date"),
'on_change_with_inventory_date',
searcher='search_inventory_date')
2016-06-30 18:54:34 +02:00
inventory_state = fields.Function(
fields.Selection('get_inventory_states', "Inventory State",
depends={'inventory'}),
2016-06-30 18:54:34 +02:00
'on_change_with_inventory_state')
2008-11-04 23:38:48 +01:00
2012-09-24 22:18:17 +02:00
@classmethod
def __setup__(cls):
super(InventoryLine, cls).__setup__()
2021-03-07 11:13:53 +01:00
cls.__access__.add('inventory')
t = cls.__table__()
2012-09-24 22:18:17 +02:00
cls._sql_constraints += [
('check_line_qty_pos', Check(t, t.quantity >= 0),
2018-12-29 14:20:29 +01:00
'stock.msg_inventory_line_quantity_positive'),
2012-09-24 22:18:17 +02:00
]
cls._order.insert(0, ('product', 'ASC'))
2008-11-04 23:38:48 +01:00
2012-09-24 22:18:17 +02:00
@staticmethod
def default_expected_quantity():
return 0.
2014-01-26 14:29:37 +01:00
@fields.depends('product')
2012-09-24 22:18:17 +02:00
def on_change_product(self):
if self.product:
2023-08-22 13:18:33 +02:00
self.unit = self.product.default_uom
2008-11-04 23:38:48 +01:00
2020-10-03 23:47:19 +02:00
@fields.depends('inventory', '_parent_inventory.location')
def on_change_with_inventory_location(self, name=None):
2023-03-26 09:49:00 +02:00
return self.inventory.location if self.inventory else None
2020-10-03 23:47:19 +02:00
@classmethod
def search_inventory_location(cls, name, clause):
nested = clause[0][len(name):]
return [('inventory.location' + nested, *clause[1:])]
2020-10-03 23:47:19 +02:00
@fields.depends('inventory', '_parent_inventory.date')
def on_change_with_inventory_date(self, name=None):
if self.inventory:
return self.inventory.date
@classmethod
def search_inventory_date(cls, name, clause):
return [('inventory.date',) + tuple(clause[1:])]
@classmethod
def get_inventory_states(cls):
pool = Pool()
Inventory = pool.get('stock.inventory')
return Inventory.fields_get(['state'])['state']['selection']
2016-06-30 18:54:34 +02:00
@fields.depends('inventory', '_parent_inventory.state')
def on_change_with_inventory_state(self, name=None):
if self.inventory:
return self.inventory.state
return 'draft'
2013-06-21 23:24:17 +02:00
def get_rec_name(self, name):
return self.product.rec_name
2017-07-26 23:43:27 +02:00
@classmethod
def search_rec_name(cls, name, clause):
2017-09-27 18:37:34 +02:00
return [('product.rec_name',) + tuple(clause[1:])]
2017-07-26 23:43:27 +02:00
2023-08-22 13:18:33 +02:00
def get_unit(self, name):
return self.product.default_uom
2008-11-04 23:38:48 +01:00
2013-06-21 23:24:17 +02:00
@property
def unique_key(self):
key = []
for fname in self.inventory.grouping():
value = getattr(self, fname)
if isinstance(value, Model):
value = value.id
key.append(value)
return tuple(key)
2013-06-21 23:24:17 +02:00
2012-09-24 22:18:17 +02:00
@classmethod
def cancel_move(cls, lines):
Move = Pool().get('stock.move')
2013-11-28 23:21:45 +01:00
moves = [m for l in lines for m in l.moves if l.moves]
Move.cancel(moves)
Move.delete(moves)
2008-11-04 23:38:48 +01:00
2013-11-28 23:21:45 +01:00
def get_move(self):
'''
2013-06-21 23:24:17 +02:00
Return Move instance for the inventory line
'''
2012-09-24 22:18:17 +02:00
pool = Pool()
Move = pool.get('stock.move')
qty = self.quantity
if qty is None:
if self.inventory.empty_quantity is None:
2018-12-29 14:20:29 +01:00
raise InventoryValidationError(
gettext('stock.msg_inventory_missing_empty_quantity',
inventory=self.inventory.rec_name))
if self.inventory.empty_quantity == 'keep':
return
else:
qty = 0.0
2023-08-22 13:18:33 +02:00
delta_qty = self.unit.round(self.expected_quantity - qty)
2008-11-04 23:38:48 +01:00
if delta_qty == 0.0:
return
2013-06-21 23:24:17 +02:00
from_location = self.inventory.location
to_location = self.inventory.location.lost_found_used
if not to_location:
raise InventoryValidationError(
gettext('stock.msg_inventory_location_missing_lost_found',
inventory=self.inventory.rec_name,
location=self.inventory.location.rec_name))
2008-11-04 23:38:48 +01:00
if delta_qty < 0:
(from_location, to_location, delta_qty) = \
(to_location, from_location, -delta_qty)
2013-11-28 23:21:45 +01:00
return Move(
2013-06-21 23:24:17 +02:00
from_location=from_location,
to_location=to_location,
quantity=delta_qty,
product=self.product,
2023-08-22 13:18:33 +02:00
unit=self.unit,
2013-06-21 23:24:17 +02:00
company=self.inventory.company,
effective_date=self.inventory.date,
2013-11-28 23:21:45 +01:00
origin=self,
2013-06-21 23:24:17 +02:00
)
2008-11-04 23:38:48 +01:00
def update_values4complete(self, quantity):
'''
Return update values to complete inventory
'''
2012-09-24 22:18:17 +02:00
values = {}
# if nothing changed, no update
2019-09-02 18:26:02 +02:00
if self.expected_quantity == quantity:
2012-09-24 22:18:17 +02:00
return values
values['expected_quantity'] = quantity
return values
2012-09-24 22:18:17 +02:00
@classmethod
def create_values4complete(cls, inventory, quantity):
'''
Return create values to complete inventory
'''
return {
'inventory': inventory.id,
'expected_quantity': quantity,
}
@classmethod
def delete(cls, lines):
for line in lines:
if line.inventory_state not in {'cancelled', 'draft'}:
2018-12-29 14:20:29 +01:00
raise AccessError(
gettext('stock.msg_inventory_line_delete_cancel',
line=line.rec_name,
inventory=line.inventory.rec_name))
super(InventoryLine, cls).delete(lines)
2018-07-27 17:07:31 +02:00
class Count(Wizard):
"Stock Inventory Count"
__name__ = 'stock.inventory.count'
start_state = 'search'
search = StateView(
'stock.inventory.count.search',
'stock.inventory_count_search_view_form', [
Button("End", 'end', 'tryton-cancel'),
2018-08-19 20:02:58 +02:00
Button("Select", 'quantity', 'tryton-forward', default=True),
2018-07-27 17:07:31 +02:00
])
quantity = StateView(
'stock.inventory.count.quantity',
'stock.inventory_count_quantity_view_form', [
Button("Cancel", 'search', 'tryton-cancel'),
Button("Add", 'add', 'tryton-ok', default=True),
])
add = StateTransition()
def default_quantity(self, fields):
pool = Pool()
InventoryLine = pool.get('stock.inventory.line')
2018-12-29 14:20:29 +01:00
Warning = pool.get('res.user.warning')
2018-07-27 17:07:31 +02:00
values = {}
lines = InventoryLine.search(
self.get_line_domain(self.record), limit=1)
2018-07-27 17:07:31 +02:00
if not lines:
warning_name = '%s.%s.count_create' % (
self.record, self.search.search)
2018-12-29 14:20:29 +01:00
if Warning.check(warning_name):
raise InventoryCountWarning(warning_name,
gettext('stock.msg_inventory_count_create_line',
search=self.search.search.rec_name))
line, = InventoryLine.create([self.get_line_values(self.record)])
2018-07-27 17:07:31 +02:00
else:
line, = lines
values['line'] = line.id
values['product'] = line.product.id
2023-08-22 13:18:33 +02:00
values['unit'] = line.unit.id
if line.unit.rounding == 1:
values['quantity'] = 1.
2018-07-27 17:07:31 +02:00
return values
def get_line_domain(self, inventory):
pool = Pool()
Product = pool.get('product.product')
domain = [
('inventory', '=', inventory.id),
]
if isinstance(self.search.search, Product):
domain.append(('product', '=', self.search.search.id))
return domain
def get_line_values(self, inventory):
pool = Pool()
Product = pool.get('product.product')
InventoryLine = pool.get('stock.inventory.line')
values = InventoryLine.create_values4complete(inventory, 0)
if isinstance(self.search.search, Product):
values['product'] = self.search.search.id
return values
def transition_add(self):
if self.quantity.line and self.quantity.quantity:
2018-07-27 17:07:31 +02:00
line = self.quantity.line
if line.quantity:
line.quantity += self.quantity.quantity
2018-07-27 17:07:31 +02:00
else:
line.quantity = self.quantity.quantity
2018-07-27 17:07:31 +02:00
line.save()
return 'search'
class CountSearch(ModelView):
"Stock Inventory Count"
__name__ = 'stock.inventory.count.search'
search = fields.Reference(
"Search", [
('product.product', "Product"),
],
required=True,
domain={
'product.product': [
('type', '=', 'goods'),
('consumable', '=', False),
],
},
2018-12-04 12:25:45 +01:00
help="The item that's counted.")
2018-07-27 17:07:31 +02:00
@classmethod
def default_search(cls):
return 'product.product,-1'
class CountQuantity(ModelView):
"Stock Inventory Count"
__name__ = 'stock.inventory.count.quantity'
line = fields.Many2One(
'stock.inventory.line', "Line", readonly=True, required=True)
product = fields.Many2One('product.product', "Product", readonly=True)
2023-08-22 13:18:33 +02:00
unit = fields.Many2One(
'product.uom', "Unit", readonly=True,
2018-12-04 12:25:45 +01:00
help="The unit in which the quantities are specified.")
total_quantity = fields.Float(
2023-08-22 13:18:33 +02:00
"Total Quantity", digits='unit', readonly=True,
2018-12-04 12:25:45 +01:00
help="The total amount of the line counted so far.")
2018-07-27 17:07:31 +02:00
quantity = fields.Float(
2023-08-22 13:18:33 +02:00
"Quantity", digits='unit', required=True,
2018-12-04 12:25:45 +01:00
help="The quantity to add to the existing count.")
2018-07-27 17:07:31 +02:00
@fields.depends('quantity', 'line')
def on_change_quantity(self):
2018-07-27 17:07:31 +02:00
if self.line:
self.total_quantity = (
(self.line.quantity or 0) + (self.quantity or 0))