1741 lines
69 KiB
Python
1741 lines
69 KiB
Python
# 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 decimal import Decimal
|
|
from functools import partial
|
|
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from trytond.model.exceptions import AccessError
|
|
from trytond.modules.company.tests import (
|
|
CompanyTestMixin, PartyCompanyCheckEraseMixin, create_company, set_company)
|
|
from trytond.modules.party.tests import PartyCheckReplaceMixin
|
|
from trytond.modules.stock.exceptions import (
|
|
LocationValidationError, MoveOriginWarning, PeriodCloseError,
|
|
ProductStockWarning)
|
|
from trytond.pool import Pool
|
|
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
|
from trytond.transaction import Transaction, check_access
|
|
|
|
|
|
class StockTestCase(
|
|
PartyCompanyCheckEraseMixin, PartyCheckReplaceMixin, CompanyTestMixin,
|
|
ModuleTestCase):
|
|
'Test Stock module'
|
|
module = 'stock'
|
|
longMessage = True
|
|
|
|
@with_transaction()
|
|
def test_move_internal_quantity(self):
|
|
'Test Move.internal_quantity'
|
|
pool = Pool()
|
|
Uom = pool.get('product.uom')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
|
|
kg, = Uom.search([('name', '=', 'Kilogram')])
|
|
g, = Uom.search([('name', '=', 'Gram')])
|
|
template, = Template.create([{
|
|
'name': 'Test Move.internal_quantity',
|
|
'type': 'goods',
|
|
'default_uom': kg.id,
|
|
}])
|
|
product, = Product.create([{
|
|
'template': template.id,
|
|
}])
|
|
supplier, = Location.search([('code', '=', 'SUP')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
company = create_company()
|
|
currency = company.currency
|
|
with set_company(company):
|
|
tests = [
|
|
(kg, 10, 10, 0),
|
|
(g, 100, 0.1, 1),
|
|
(g, 1, 0, 0), # rounded
|
|
(kg, 35.23, 35.23, 2), # check infinite loop
|
|
]
|
|
for unit, quantity, internal_quantity, ndigits in tests:
|
|
move, = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': quantity,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}])
|
|
self.assertEqual(round(move.internal_quantity, ndigits),
|
|
internal_quantity)
|
|
|
|
for unit, quantity, internal_quantity, ndigits in tests:
|
|
Move.write([move], {
|
|
'unit': unit.id,
|
|
'quantity': quantity,
|
|
})
|
|
self.assertEqual(round(move.internal_quantity, ndigits),
|
|
internal_quantity)
|
|
|
|
@with_transaction()
|
|
def test_products_by_location(self):
|
|
'Test products_by_location'
|
|
pool = Pool()
|
|
Uom = pool.get('product.uom')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
Period = pool.get('stock.period')
|
|
transaction = Transaction()
|
|
|
|
kg, = Uom.search([('name', '=', 'Kilogram')])
|
|
g, = Uom.search([('name', '=', 'Gram')])
|
|
template, = Template.create([{
|
|
'name': 'Test products_by_location',
|
|
'type': 'goods',
|
|
'default_uom': kg.id,
|
|
}])
|
|
product, = Product.create([{
|
|
'template': template.id,
|
|
}])
|
|
supplier, = Location.search([('code', '=', 'SUP')])
|
|
customer, = Location.search([('code', '=', 'CUS')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
storage_empty, = Location.copy([storage])
|
|
company = create_company()
|
|
currency = company.currency
|
|
with set_company(company):
|
|
today = datetime.date.today()
|
|
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': kg.id,
|
|
'quantity': 5,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'planned_date': today + relativedelta(days=-5),
|
|
'effective_date': today + relativedelta(days=-5),
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': kg.id,
|
|
'quantity': 1,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'planned_date': today + relativedelta(days=-4),
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': kg.id,
|
|
'quantity': 1,
|
|
'from_location': storage.id,
|
|
'to_location': customer.id,
|
|
'planned_date': today,
|
|
'effective_date': today,
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': kg.id,
|
|
'quantity': 1,
|
|
'from_location': storage.id,
|
|
'to_location': customer.id,
|
|
'planned_date': today,
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': kg.id,
|
|
'quantity': 2,
|
|
'from_location': storage.id,
|
|
'to_location': customer.id,
|
|
'planned_date': today + relativedelta(days=5),
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': kg.id,
|
|
'quantity': 5,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'planned_date': today + relativedelta(days=7),
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}])
|
|
Move.do([moves[0], moves[2]])
|
|
|
|
products_by_location = partial(Product.products_by_location,
|
|
[storage.id], grouping_filter=([product.id],))
|
|
|
|
tests = [
|
|
({'stock_date_end': today + relativedelta(days=-6),
|
|
}, 0),
|
|
({'stock_date_end': today + relativedelta(days=-5),
|
|
}, 5),
|
|
({'stock_date_end': today + relativedelta(days=-4),
|
|
}, 5),
|
|
({'stock_date_end': today + relativedelta(days=-3),
|
|
}, 5),
|
|
({'stock_date_end': today,
|
|
}, 4),
|
|
({'stock_date_end': today + relativedelta(days=1),
|
|
}, 3),
|
|
({'stock_date_end': today + relativedelta(days=5),
|
|
}, 1),
|
|
({'stock_date_end': today + relativedelta(days=6),
|
|
}, 1),
|
|
({'stock_date_end': today + relativedelta(days=7),
|
|
}, 6),
|
|
({'stock_date_end': today + relativedelta(days=8),
|
|
}, 6),
|
|
({'stock_date_end': False,
|
|
}, 6),
|
|
({'stock_date_end': today + relativedelta(days=-6),
|
|
'forecast': True,
|
|
}, 0),
|
|
({'stock_date_end': today + relativedelta(days=-5),
|
|
'forecast': True,
|
|
}, 5),
|
|
({'stock_date_end': today + relativedelta(days=-4),
|
|
'forecast': True,
|
|
}, 5),
|
|
({'stock_date_end': today + relativedelta(days=-3),
|
|
'forecast': True,
|
|
}, 5),
|
|
({'stock_date_end': today,
|
|
'forecast': True,
|
|
}, 3),
|
|
({'stock_date_end': today + relativedelta(days=1),
|
|
'forecast': True,
|
|
}, 3),
|
|
({'stock_date_end': today + relativedelta(days=5),
|
|
'forecast': True,
|
|
}, 1),
|
|
({'stock_date_end': today + relativedelta(days=6),
|
|
'forecast': True,
|
|
}, 1),
|
|
({'stock_date_end': today + relativedelta(days=7),
|
|
'forecast': True,
|
|
}, 6),
|
|
({'stock_date_end': False,
|
|
'forecast': True,
|
|
}, 6),
|
|
]
|
|
today_quantity = 4
|
|
|
|
def tests_product_quantity(context, quantity):
|
|
product_reloaded = Product(product.id)
|
|
if (not context.get('stock_date_end')
|
|
or context['stock_date_end'] > today):
|
|
self.assertEqual(
|
|
product_reloaded.forecast_quantity, quantity,
|
|
msg='context %r' % context)
|
|
self.assertEqual(
|
|
product_reloaded.quantity, today_quantity,
|
|
msg='context %r' % context)
|
|
elif context.get('forecast'):
|
|
self.assertEqual(
|
|
product_reloaded.forecast_quantity, quantity,
|
|
msg='context %r' % context)
|
|
elif context.get('stock_date_end') == today:
|
|
self.assertEqual(product_reloaded.quantity, quantity,
|
|
msg='context %r' % context)
|
|
else:
|
|
self.assertEqual(
|
|
product_reloaded.forecast_quantity, quantity,
|
|
msg='context %r' % context)
|
|
self.assertEqual(product_reloaded.quantity, quantity,
|
|
msg='context %r' % context)
|
|
|
|
def tests_product_search_quantity(context, quantity):
|
|
if (not context.get('stock_date_end')
|
|
or context['stock_date_end'] > today
|
|
or context.get('forecast')):
|
|
fname = 'forecast_quantity'
|
|
else:
|
|
fname = 'quantity'
|
|
found_products = Product.search([
|
|
(fname, '=', quantity),
|
|
])
|
|
self.assertIn(product, found_products)
|
|
|
|
found_products = Product.search([
|
|
(fname, '!=', quantity),
|
|
])
|
|
self.assertNotIn(product, found_products)
|
|
|
|
found_products = Product.search([
|
|
(fname, 'in', (quantity, quantity + 1)),
|
|
])
|
|
self.assertIn(product, found_products)
|
|
|
|
found_products = Product.search([
|
|
(fname, 'not in', (quantity, quantity + 1)),
|
|
])
|
|
self.assertNotIn(product, found_products)
|
|
|
|
found_products = Product.search([
|
|
(fname, '<', quantity),
|
|
])
|
|
self.assertNotIn(product, found_products)
|
|
found_products = Product.search([
|
|
(fname, '<', quantity + 1),
|
|
])
|
|
self.assertIn(product, found_products)
|
|
|
|
found_products = Product.search([
|
|
(fname, '>', quantity),
|
|
])
|
|
self.assertNotIn(product, found_products)
|
|
found_products = Product.search([
|
|
(fname, '>', quantity - 1),
|
|
])
|
|
self.assertIn(product, found_products)
|
|
|
|
found_products = Product.search([
|
|
(fname, '>=', quantity),
|
|
])
|
|
self.assertIn(product, found_products)
|
|
|
|
found_products = Product.search([
|
|
(fname, '<=', quantity),
|
|
])
|
|
self.assertIn(product, found_products)
|
|
|
|
def test_products_by_location():
|
|
for context, quantity in tests:
|
|
with transaction.set_context(context):
|
|
if not quantity:
|
|
self.assertEqual(products_by_location(), {})
|
|
else:
|
|
self.assertEqual(products_by_location(),
|
|
{(storage.id, product.id): quantity})
|
|
with transaction.set_context(
|
|
locations=[storage.id]):
|
|
tests_product_quantity(context, quantity)
|
|
tests_product_search_quantity(
|
|
context, quantity)
|
|
with transaction.set_context(
|
|
locations=[storage.id, storage_empty.id]):
|
|
tests_product_quantity(context, quantity)
|
|
tests_product_search_quantity(
|
|
context, quantity)
|
|
|
|
test_products_by_location()
|
|
|
|
periods = [
|
|
today + relativedelta(days=-6),
|
|
today + relativedelta(days=-5),
|
|
today + relativedelta(days=-4),
|
|
today + relativedelta(days=-3),
|
|
today + relativedelta(days=-2),
|
|
]
|
|
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': g.id,
|
|
'quantity': 1,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'planned_date': today + relativedelta(days=-5),
|
|
'effective_date': (today
|
|
+ relativedelta(days=-5)),
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}])
|
|
Move.do(moves)
|
|
# Nothing should change when adding a small quantity
|
|
test_products_by_location()
|
|
|
|
for period_date in periods:
|
|
period, = Period.create([{
|
|
'date': period_date,
|
|
'company': company.id,
|
|
}])
|
|
Period.close([period])
|
|
test_products_by_location()
|
|
|
|
@with_transaction()
|
|
def test_products_by_location_with_childs(self):
|
|
'Test products_by_location with_childs and stock_skip_warehouse'
|
|
pool = Pool()
|
|
Uom = pool.get('product.uom')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
|
|
unit, = Uom.search([('name', '=', 'Unit')])
|
|
template, = Template.create([{
|
|
'name': 'Test products_by_location',
|
|
'type': 'goods',
|
|
'default_uom': unit.id,
|
|
}])
|
|
product, = Product.create([{
|
|
'template': template.id,
|
|
}])
|
|
|
|
lost_found, = Location.search([('type', '=', 'lost_found')])
|
|
warehouse, = Location.search([('type', '=', 'warehouse')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
input_, = Location.search([('code', '=', 'IN')])
|
|
storage1, = Location.create([{
|
|
'name': 'Storage 1',
|
|
'type': 'view',
|
|
'parent': storage.id,
|
|
}])
|
|
storage2, = Location.create([{
|
|
'name': 'Storage 1.1',
|
|
'type': 'view',
|
|
'parent': storage1.id,
|
|
}])
|
|
storage3, = Location.create([{
|
|
'name': 'Storage 2',
|
|
'type': 'view',
|
|
'parent': storage.id,
|
|
}])
|
|
company = create_company()
|
|
with set_company(company):
|
|
today = datetime.date.today()
|
|
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': lost_found.id,
|
|
'to_location': storage.id,
|
|
'planned_date': today,
|
|
'effective_date': today,
|
|
'company': company.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': input_.id,
|
|
'to_location': storage.id,
|
|
'planned_date': today,
|
|
'effective_date': today,
|
|
'company': company.id,
|
|
}])
|
|
Move.do(moves)
|
|
|
|
products_by_location = Product.products_by_location(
|
|
[warehouse.id],
|
|
grouping_filter=([product.id],),
|
|
with_childs=True)
|
|
self.assertEqual(products_by_location[(warehouse.id, product.id)],
|
|
1)
|
|
products_by_location = Product.products_by_location(
|
|
[warehouse.id, storage.id],
|
|
grouping_filter=([product.id],),
|
|
with_childs=True)
|
|
self.assertEqual(
|
|
products_by_location[(warehouse.id, product.id)], 1)
|
|
self.assertEqual(products_by_location[(storage.id, product.id)], 2)
|
|
|
|
with Transaction().set_context(locations=[warehouse.id]):
|
|
found_products = Product.search([
|
|
('quantity', '=', 1),
|
|
])
|
|
self.assertListEqual([product], found_products)
|
|
|
|
with Transaction().set_context(stock_skip_warehouse=True):
|
|
products_by_location = Product.products_by_location(
|
|
[warehouse.id],
|
|
grouping_filter=([product.id],),
|
|
with_childs=True)
|
|
products_by_location_all = Product.products_by_location(
|
|
[warehouse.id], with_childs=True)
|
|
self.assertEqual(
|
|
products_by_location[(warehouse.id, product.id)], 2)
|
|
self.assertEqual(
|
|
products_by_location_all[(warehouse.id, product.id)], 2)
|
|
|
|
with Transaction().set_context(locations=[warehouse.id]):
|
|
found_products = Product.search([
|
|
('quantity', '=', 2),
|
|
])
|
|
self.assertListEqual([product], found_products)
|
|
|
|
@with_transaction()
|
|
def test_products_by_location_flat_childs(self, period_closed=False):
|
|
"Test products_by_location on flat_childs"
|
|
pool = Pool()
|
|
Uom = pool.get('product.uom')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
Date = pool.get('ir.date')
|
|
Period = pool.get('stock.period')
|
|
|
|
unit, = Uom.search([('name', '=', 'Unit')])
|
|
template, = Template.create([{
|
|
'name': "Product",
|
|
'type': 'goods',
|
|
'default_uom': unit.id,
|
|
}])
|
|
product, = Product.create([{
|
|
'template': template.id,
|
|
}])
|
|
|
|
lost_found, = Location.search([('type', '=', 'lost_found')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
storage.flat_childs = True
|
|
storage.save()
|
|
storage1, = Location.create([{
|
|
'name': 'Storage 1',
|
|
'type': 'storage',
|
|
'parent': storage.id,
|
|
}])
|
|
storage2, = Location.create([{
|
|
'name': 'Storage 2',
|
|
'type': 'storage',
|
|
'parent': storage.id,
|
|
}])
|
|
|
|
company = create_company()
|
|
with set_company(company):
|
|
date = Date.today() - relativedelta(days=1)
|
|
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': lost_found.id,
|
|
'to_location': storage.id,
|
|
'planned_date': date,
|
|
'effective_date': date,
|
|
'company': company.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': lost_found.id,
|
|
'to_location': storage1.id,
|
|
'planned_date': date,
|
|
'effective_date': date,
|
|
'company': company.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': storage1.id,
|
|
'to_location': storage2.id,
|
|
'planned_date': date,
|
|
'effective_date': date,
|
|
'company': company.id,
|
|
}])
|
|
Move.do(moves)
|
|
|
|
if period_closed:
|
|
period, = Period.create([{
|
|
'date': date,
|
|
'company': company.id,
|
|
}])
|
|
Period.close([period])
|
|
|
|
# Test flat location
|
|
products_by_location = Product.products_by_location(
|
|
[storage.id],
|
|
grouping_filter=([product.id],),
|
|
with_childs=True)
|
|
self.assertEqual(
|
|
products_by_location[(storage.id, product.id)], 2)
|
|
|
|
# Test mixed flat and nested location
|
|
products_by_location = Product.products_by_location(
|
|
[storage.parent.id, storage.id, storage1.id, storage2.id],
|
|
grouping_filter=([product.id],),
|
|
with_childs=True)
|
|
self.assertEqual(
|
|
products_by_location[(storage.parent.id, product.id)], 2)
|
|
self.assertEqual(
|
|
products_by_location[(storage.id, product.id)], 2)
|
|
self.assertEqual(
|
|
products_by_location[(storage1.id, product.id)], 0)
|
|
self.assertEqual(
|
|
products_by_location[(storage2.id, product.id)], 1)
|
|
|
|
# Test non flat
|
|
products_by_location = Product.products_by_location(
|
|
[lost_found.id],
|
|
grouping_filter=([product.id],),
|
|
with_childs=True)
|
|
self.assertEqual(
|
|
products_by_location[(lost_found.id, product.id)], -2)
|
|
|
|
def test_products_by_location_flat_childs_period_closed(self):
|
|
"Test products_by_location on flat_childs with period closed"
|
|
self.test_products_by_location_flat_childs(period_closed=True)
|
|
|
|
@with_transaction()
|
|
def test_products_by_location_2nd_level_flat_childs(self):
|
|
"Test products_by_location on 2nd level flat_childs"
|
|
pool = Pool()
|
|
Uom = pool.get('product.uom')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
Date = pool.get('ir.date')
|
|
|
|
unit, = Uom.search([('name', '=', 'Unit')])
|
|
template, = Template.create([{
|
|
'name': "Product",
|
|
'type': 'goods',
|
|
'default_uom': unit.id,
|
|
}])
|
|
product, = Product.create([{
|
|
'template': template.id,
|
|
}])
|
|
|
|
supplier, = Location.search([('code', '=', 'SUP')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
storage1, = Location.create([{
|
|
'name': 'Storage 1',
|
|
'type': 'storage',
|
|
'flat_childs': True,
|
|
'parent': storage.id,
|
|
}])
|
|
storage2, = Location.create([{
|
|
'name': 'Storage 2',
|
|
'type': 'storage',
|
|
'parent': storage1.id,
|
|
}])
|
|
|
|
company = create_company()
|
|
with set_company(company):
|
|
date = Date.today() - relativedelta(days=1)
|
|
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 80,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'effective_date': date,
|
|
'company': company.id,
|
|
'unit_price': 10.0,
|
|
'currency': company.currency.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 60,
|
|
'from_location': storage.id,
|
|
'to_location': storage2.id,
|
|
'effective_date': date,
|
|
'company': company.id,
|
|
}])
|
|
Move.do(moves)
|
|
|
|
# Test 2nd level location
|
|
products_by_location = Product.products_by_location(
|
|
[storage.id],
|
|
grouping_filter=([product.id],),
|
|
with_childs=True)
|
|
self.assertEqual(
|
|
products_by_location[(storage.id, product.id)], 80)
|
|
|
|
# Test 1st level and 2nd level nested locations
|
|
products_by_location = Product.products_by_location(
|
|
[storage.id, storage1.id],
|
|
grouping_filter=([product.id],),
|
|
with_childs=True)
|
|
self.assertEqual(
|
|
products_by_location[(storage.id, product.id)], 80)
|
|
self.assertEqual(
|
|
products_by_location[(storage1.id, product.id)], 60)
|
|
|
|
# Test mixed flat and 2nd level nested locations
|
|
products_by_location = Product.products_by_location(
|
|
[storage.id, storage2.id],
|
|
grouping_filter=([product.id],),
|
|
with_childs=True)
|
|
self.assertEqual(
|
|
products_by_location[(storage.id, product.id)], 80)
|
|
self.assertEqual(
|
|
products_by_location[(storage2.id, product.id)], 60)
|
|
|
|
@with_transaction()
|
|
def test_products_by_location_grouped_by_date(self):
|
|
"Test products_by_location grouped by date"
|
|
pool = Pool()
|
|
Uom = pool.get('product.uom')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
Date = pool.get('ir.date')
|
|
|
|
unit, = Uom.search([('name', '=', 'Unit')])
|
|
template, = Template.create([{
|
|
'name': "Template",
|
|
'type': 'goods',
|
|
'default_uom': unit.id,
|
|
}])
|
|
product, = Product.create([{
|
|
'template': template.id,
|
|
}])
|
|
lost_found, = Location.search([('type', '=', 'lost_found')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
output, = Location.search([('code', '=', 'OUT')])
|
|
company = create_company()
|
|
with set_company(company):
|
|
today = Date.today()
|
|
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 10,
|
|
'from_location': lost_found.id,
|
|
'to_location': storage.id,
|
|
'effective_date': today,
|
|
'company': company.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 2,
|
|
'from_location': storage.id,
|
|
'to_location': output.id,
|
|
'effective_date': today + relativedelta(days=1),
|
|
'company': company.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 3,
|
|
'from_location': storage.id,
|
|
'to_location': output.id,
|
|
'effective_date': today + relativedelta(days=5),
|
|
'company': company.id,
|
|
}])
|
|
with Transaction().set_context(_skip_warnings=True):
|
|
Move.do(moves)
|
|
|
|
with Transaction().set_context(stock_date_start=today):
|
|
products_by_location = Product.products_by_location(
|
|
[storage.id],
|
|
grouping=('date', 'product'),
|
|
grouping_filter=(None, [product.id]))
|
|
|
|
self.assertDictEqual(products_by_location, {
|
|
(storage.id,
|
|
today, product.id): 10,
|
|
(storage.id,
|
|
today + relativedelta(days=1), product.id): -2,
|
|
(storage.id,
|
|
today + relativedelta(days=5), product.id): -3,
|
|
})
|
|
|
|
@with_transaction()
|
|
def test_templates_by_location(self, period_closed=False):
|
|
"Test products_by_location grouped by template"
|
|
pool = Pool()
|
|
Uom = pool.get('product.uom')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
Period = pool.get('stock.period')
|
|
Date = pool.get('ir.date')
|
|
|
|
unit, = Uom.search([('name', '=', 'Unit')])
|
|
template, = Template.create([{
|
|
'name': 'Template',
|
|
'type': 'goods',
|
|
'default_uom': unit.id,
|
|
}])
|
|
product1, product2 = Product.create([{
|
|
'template': template.id,
|
|
}] * 2)
|
|
|
|
lost_found, = Location.search([('type', '=', 'lost_found')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
input_, = Location.search([('code', '=', 'IN')])
|
|
company = create_company()
|
|
with set_company(company):
|
|
date = Date.today() - relativedelta(days=1)
|
|
|
|
moves = Move.create([{
|
|
'product': product1.id,
|
|
'unit': unit.id,
|
|
'quantity': 2,
|
|
'from_location': lost_found.id,
|
|
'to_location': storage.id,
|
|
'planned_date': date,
|
|
'effective_date': date,
|
|
'company': company.id,
|
|
}, {
|
|
'product': product2.id,
|
|
'unit': unit.id,
|
|
'quantity': 3,
|
|
'from_location': input_.id,
|
|
'to_location': storage.id,
|
|
'planned_date': date,
|
|
'effective_date': date,
|
|
'company': company.id,
|
|
}])
|
|
Move.do(moves)
|
|
|
|
if period_closed:
|
|
period, = Period.create([{
|
|
'date': date,
|
|
'company': company.id,
|
|
}])
|
|
Period.close([period])
|
|
|
|
templates_by_location = Product.products_by_location(
|
|
[storage.id],
|
|
grouping=('product.template',),
|
|
grouping_filter=([template.id],))
|
|
|
|
self.assertDictEqual(templates_by_location, {
|
|
(storage.id, template.id): 5,
|
|
})
|
|
|
|
def test_templates_by_location_period_closed(self):
|
|
"Test products_by_location grouped by template with period closed"
|
|
self.test_templates_by_location(period_closed=True)
|
|
|
|
@with_transaction()
|
|
def test_period(self):
|
|
'Test period'
|
|
pool = Pool()
|
|
Uom = pool.get('product.uom')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
Period = pool.get('stock.period')
|
|
transaction = Transaction()
|
|
|
|
unit, = Uom.search([('name', '=', 'Unit')])
|
|
template, = Template.create([{
|
|
'name': 'Test period',
|
|
'type': 'goods',
|
|
'default_uom': unit.id,
|
|
}])
|
|
product, = Product.create([{
|
|
'template': template.id,
|
|
}])
|
|
supplier, = Location.search([('code', '=', 'SUP')])
|
|
customer, = Location.search([('code', '=', 'CUS')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
company = create_company()
|
|
currency = company.currency
|
|
with set_company(company):
|
|
today = datetime.date.today()
|
|
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 10,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'planned_date': today + relativedelta(days=-5),
|
|
'effective_date': today + relativedelta(days=-5),
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 15,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'planned_date': today + relativedelta(days=-4),
|
|
'effective_date': today + relativedelta(days=-4),
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 5,
|
|
'from_location': storage.id,
|
|
'to_location': customer.id,
|
|
'planned_date': today + relativedelta(days=-3),
|
|
'effective_date': today + relativedelta(days=-3),
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}])
|
|
Move.do(moves)
|
|
Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 3,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'planned_date': None,
|
|
'effective_date': None,
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}])
|
|
|
|
tests = [
|
|
(-5, {
|
|
supplier.id: -10,
|
|
storage.id: 10,
|
|
}),
|
|
(-3, {
|
|
supplier.id: -25,
|
|
storage.id: 20,
|
|
customer.id: 5,
|
|
})
|
|
]
|
|
|
|
products_by_location = partial(Product.products_by_location,
|
|
[storage.id], grouping_filter=([product.id],))
|
|
|
|
tests_pbl = [
|
|
({'stock_date_end': today + relativedelta(days=-6)}, 0),
|
|
({'stock_date_end': today + relativedelta(days=-5)}, 10),
|
|
({'stock_date_end': today + relativedelta(days=-4)}, 25),
|
|
({'stock_date_end': today + relativedelta(days=-3)}, 20),
|
|
({'stock_date_end': today + relativedelta(days=-2)}, 20),
|
|
({'stock_date_end': today + relativedelta(days=-1)}, 20),
|
|
({'stock_date_end': today}, 20),
|
|
({'stock_date_end': datetime.date.max}, 23),
|
|
]
|
|
|
|
def test_products_by_location():
|
|
for context, quantity in tests_pbl:
|
|
with transaction.set_context(context):
|
|
if not quantity:
|
|
self.assertEqual(products_by_location(), {})
|
|
else:
|
|
self.assertEqual(products_by_location(),
|
|
{(storage.id, product.id): quantity})
|
|
|
|
test_products_by_location()
|
|
for days, quantities in tests:
|
|
period, = Period.create([{
|
|
'date': today + relativedelta(days=days),
|
|
'company': company.id,
|
|
}])
|
|
Period.close([period])
|
|
|
|
self.assertEqual(period.state, 'closed')
|
|
|
|
caches = period.caches
|
|
for cache in caches:
|
|
self.assertEqual(cache.product, product)
|
|
self.assertEqual(cache.internal_quantity,
|
|
quantities[cache.location.id])
|
|
|
|
test_products_by_location()
|
|
|
|
# Test check_period_closed
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 10,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'planned_date': today,
|
|
'effective_date': today,
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}])
|
|
Move.do(moves)
|
|
|
|
self.assertRaises(AccessError, Move.create, [{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 10,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'planned_date': today + relativedelta(days=-5),
|
|
'effective_date': today + relativedelta(days=-5),
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}])
|
|
self.assertRaises(AccessError, Move.create, [{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 10,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'planned_date': today + relativedelta(days=-3),
|
|
'effective_date': today + relativedelta(days=-3),
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': currency.id,
|
|
}])
|
|
|
|
# Test close period check
|
|
period, = Period.create([{
|
|
'date': today,
|
|
'company': company.id,
|
|
}])
|
|
self.assertRaises(PeriodCloseError, Period.close, [period])
|
|
|
|
period, = Period.create([{
|
|
'date': today + relativedelta(days=1),
|
|
'company': company.id,
|
|
}])
|
|
self.assertRaises(PeriodCloseError, Period.close, [period])
|
|
|
|
@with_transaction()
|
|
def test_check_origin(self):
|
|
'Test Move check_origin'
|
|
pool = Pool()
|
|
Uom = pool.get('product.uom')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
|
|
unit, = Uom.search([('name', '=', 'Unit')])
|
|
template, = Template.create([{
|
|
'name': 'Test Move.check_origin',
|
|
'type': 'goods',
|
|
'default_uom': unit.id,
|
|
}])
|
|
product, = Product.create([{
|
|
'template': template.id,
|
|
}])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
customer, = Location.search([('code', '=', 'CUS')])
|
|
company = create_company()
|
|
with set_company(company):
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': storage.id,
|
|
'to_location': customer.id,
|
|
'company': company.id,
|
|
'unit_price': Decimal(1),
|
|
'currency': company.currency.id,
|
|
}])
|
|
|
|
Move.check_origin(moves, set())
|
|
Move.check_origin(moves, {'supplier'})
|
|
self.assertRaises(MoveOriginWarning, Move.check_origin, moves,
|
|
{'customer'})
|
|
|
|
def test_assign_try(self):
|
|
'Test Move assign_try'
|
|
for quantity, quantities, success, result in [
|
|
(10, [2], True, {'assigned': [2]}),
|
|
(5, [10], False, {'assigned': [5], 'draft': [5]}),
|
|
(0, [3], False, {'draft': [3]}),
|
|
(6.8, [2.1, 1.7, 1.2, 1.8], True,
|
|
{'assigned': sorted([2.1, 1.7, 1.2, 1.8])}),
|
|
]:
|
|
self._test_assign_try(quantity, quantities, success, result)
|
|
|
|
@with_transaction()
|
|
def _test_assign_try(self, quantity, quantities, success, result):
|
|
pool = Pool()
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Uom = pool.get('product.uom')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
|
|
unit, = Uom.search([('name', '=', 'Meter')])
|
|
template = Template(
|
|
name='Test Move.assign_try',
|
|
type='goods',
|
|
default_uom=unit,
|
|
)
|
|
template.save()
|
|
product = Product(template=template.id)
|
|
product.save()
|
|
|
|
supplier, = Location.search([('code', '=', 'SUP')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
customer, = Location.search([('code', '=', 'CUS')])
|
|
|
|
company = create_company()
|
|
with set_company(company):
|
|
move, = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': quantity,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'company': company.id,
|
|
'unit_price': Decimal(1),
|
|
'currency': company.currency.id,
|
|
}])
|
|
Move.do([move])
|
|
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': qty,
|
|
'from_location': storage.id,
|
|
'to_location': customer.id,
|
|
'company': company.id,
|
|
'unit_price': Decimal(1),
|
|
'currency': company.currency.id,
|
|
} for qty in quantities])
|
|
|
|
msg = 'quantity: %s, quantities: %s' % (quantity, quantities)
|
|
self.assertEqual(Move.assign_try(moves), success, msg=msg)
|
|
moves = Move.search([
|
|
('product', '=', product.id),
|
|
('from_location', '=', storage.id),
|
|
('to_location', '=', customer.id),
|
|
('company', '=', company.id),
|
|
])
|
|
states = defaultdict(list)
|
|
for move in moves:
|
|
states[move.state].append(move.quantity)
|
|
for state in states:
|
|
states[state].sort()
|
|
self.assertEqual(states, result, msg=msg)
|
|
|
|
@with_transaction()
|
|
def test_assign_try_chained(self):
|
|
"Test Move assign_try chained"
|
|
pool = Pool()
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Uom = pool.get('product.uom')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
|
|
unit, = Uom.search([('name', '=', 'Meter')])
|
|
template = Template(
|
|
name='Test Move.assign_try',
|
|
type='goods',
|
|
default_uom=unit,
|
|
)
|
|
template.save()
|
|
product = Product(template=template.id)
|
|
product.save()
|
|
|
|
supplier, = Location.search([('code', '=', 'SUP')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
storage2, = Location.copy([storage])
|
|
storage3, = Location.copy([storage])
|
|
|
|
company = create_company()
|
|
with set_company(company):
|
|
move, = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'company': company.id,
|
|
'unit_price': Decimal(1),
|
|
'currency': company.currency.id,
|
|
}])
|
|
Move.do([move])
|
|
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': from_.id,
|
|
'to_location': to.id,
|
|
'company': company.id,
|
|
} for from_, to in [
|
|
(storage, storage2),
|
|
(storage2, storage3)]])
|
|
|
|
self.assertFalse(Move.assign_try(moves))
|
|
self.assertEqual([m.state for m in moves], ['assigned', 'draft'])
|
|
|
|
@with_transaction()
|
|
def test_assign_try_skip_to_location(self):
|
|
"Test Move assign_try skip to_location"
|
|
pool = Pool()
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Uom = pool.get('product.uom')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
|
|
unit, = Uom.search([('name', '=', 'Meter')])
|
|
template = Template(
|
|
name='Test Move.assign_try',
|
|
type='goods',
|
|
default_uom=unit,
|
|
)
|
|
template.save()
|
|
product = Product(template=template.id)
|
|
product.save()
|
|
|
|
supplier, = Location.search([('code', '=', 'SUP')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
child, = Location.copy([storage], default={'parent': storage.id})
|
|
|
|
company = create_company()
|
|
with set_company(company):
|
|
move, = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': supplier.id,
|
|
'to_location': child.id,
|
|
'company': company.id,
|
|
'unit_price': Decimal(1),
|
|
'currency': company.currency.id,
|
|
}])
|
|
Move.do([move])
|
|
|
|
move, = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': storage.id,
|
|
'to_location': child.id,
|
|
'company': company.id,
|
|
}])
|
|
|
|
self.assertFalse(Move.assign_try([move]))
|
|
self.assertEqual(move.state, 'draft')
|
|
|
|
@with_transaction()
|
|
def test_assign_try_prefer_from_location(self):
|
|
"Test Move assign_try prefer from location"
|
|
pool = Pool()
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Uom = pool.get('product.uom')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
|
|
unit, = Uom.search([('name', '=', 'Meter')])
|
|
template = Template(
|
|
name='Test Move.assign_try',
|
|
type='goods',
|
|
default_uom=unit,
|
|
)
|
|
template.save()
|
|
product = Product(template=template.id)
|
|
product.save()
|
|
|
|
supplier, = Location.search([('code', '=', 'SUP')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
# Ensure storage2 comes first when ordering locations by name
|
|
storage2, = Location.copy(
|
|
[storage], default={'name': "AAA", 'parent': storage.id})
|
|
customer, = Location.search([('code', '=', 'CUS')])
|
|
|
|
company = create_company()
|
|
with set_company(company):
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'company': company.id,
|
|
'unit_price': Decimal(1),
|
|
'currency': company.currency.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': supplier.id,
|
|
'to_location': storage2.id,
|
|
'company': company.id,
|
|
'unit_price': Decimal(1),
|
|
'currency': company.currency.id,
|
|
}])
|
|
Move.do(moves)
|
|
|
|
move, = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': storage.id,
|
|
'to_location': customer.id,
|
|
'company': company.id,
|
|
'unit_price': Decimal(1),
|
|
'currency': company.currency.id,
|
|
}])
|
|
|
|
Move.assign_try([move])
|
|
|
|
self.assertEqual(move.state, 'assigned')
|
|
self.assertEqual(move.from_location, storage)
|
|
|
|
@with_transaction()
|
|
def test_assign_without_moves(self):
|
|
"Test Move assign_try with empty moves"
|
|
pool = Pool()
|
|
Move = pool.get('stock.move')
|
|
|
|
self.assertTrue(Move.assign_try([]))
|
|
|
|
@with_transaction()
|
|
def test_assign_try_modified_move(self):
|
|
"Test Move assign_try with modified move"
|
|
pool = Pool()
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Uom = pool.get('product.uom')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
|
|
unit, = Uom.search([('name', '=', 'Meter')])
|
|
template = Template(
|
|
name='Test Move.assign_try',
|
|
type='goods',
|
|
default_uom=unit,
|
|
)
|
|
template.save()
|
|
product = Product(template=template.id)
|
|
product.save()
|
|
|
|
supplier, = Location.search([('code', '=', 'SUP')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
storage2, = Location.copy([storage])
|
|
customer, = Location.search([('code', '=', 'CUS')])
|
|
|
|
company = create_company()
|
|
with set_company(company):
|
|
move, = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'company': company.id,
|
|
'unit_price': Decimal(1),
|
|
'currency': company.currency.id,
|
|
}])
|
|
Move.do([move])
|
|
|
|
move, = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 2,
|
|
'from_location': storage2.id,
|
|
'to_location': customer.id,
|
|
'company': company.id,
|
|
'unit_price': Decimal(1),
|
|
'currency': company.currency.id,
|
|
}])
|
|
|
|
# Assign from a different location
|
|
move.from_location = storage
|
|
self.assertFalse(Move.assign_try([move]))
|
|
|
|
move, = Move.search([
|
|
('to_location', '=', customer.id),
|
|
('state', '=', 'assigned'),
|
|
])
|
|
self.assertEqual(move.from_location, storage)
|
|
self.assertEqual(move.quantity, 1)
|
|
|
|
move, = Move.search([
|
|
('to_location', '=', customer.id),
|
|
('state', '=', 'draft'),
|
|
])
|
|
# Remaining move has the original location
|
|
self.assertEqual(move.from_location, storage2)
|
|
self.assertEqual(move.quantity, 1)
|
|
|
|
@with_transaction()
|
|
def test_products_by_location_assign(self):
|
|
"Test products by location for assignation"
|
|
pool = Pool()
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Uom = pool.get('product.uom')
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
today = datetime.date.today()
|
|
|
|
unit, = Uom.search([('name', '=', 'Unit')])
|
|
template = Template(
|
|
name="Product",
|
|
type='goods',
|
|
default_uom=unit,
|
|
)
|
|
template.save()
|
|
product, = Product.create([{'template': template.id}])
|
|
|
|
supplier, = Location.search([('code', '=', 'SUP')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
customer, = Location.search([('code', '=', 'CUS')])
|
|
|
|
company = create_company()
|
|
with set_company(company):
|
|
move_supplier, = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 10,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'planned_date': today,
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': company.currency.id,
|
|
}])
|
|
move_customer, = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 5,
|
|
'from_location': storage.id,
|
|
'to_location': customer.id,
|
|
'planned_date': today + relativedelta(days=1),
|
|
'company': company.id,
|
|
'unit_price': Decimal('1'),
|
|
'currency': company.currency.id,
|
|
}])
|
|
|
|
Move.assign([move_supplier])
|
|
with Transaction().set_context(
|
|
stock_date_end=today,
|
|
stock_assign=True):
|
|
pbl = Product.products_by_location(
|
|
[storage.id], grouping_filter=([product.id],))
|
|
self.assertDictEqual(pbl, {})
|
|
|
|
Move.assign([move_customer])
|
|
with Transaction().set_context(
|
|
stock_date_end=today,
|
|
stock_assign=True):
|
|
pbl = Product.products_by_location(
|
|
[storage.id], grouping_filter=([product.id],))
|
|
self.assertDictEqual(pbl, {(storage.id, product.id): -5})
|
|
|
|
Move.do([move_supplier])
|
|
with Transaction().set_context(
|
|
stock_date_end=today,
|
|
stock_assign=True):
|
|
pbl = Product.products_by_location(
|
|
[storage.id], grouping_filter=([product.id],))
|
|
self.assertDictEqual(pbl, {(storage.id, product.id): 5})
|
|
|
|
with Transaction().set_context(
|
|
stock_date_end=today + relativedelta(days=1),
|
|
stock_assign=True):
|
|
pbl = Product.products_by_location(
|
|
[storage.id], grouping_filter=([product.id],))
|
|
self.assertDictEqual(pbl, {(storage.id, product.id): 5})
|
|
|
|
with Transaction().set_context(
|
|
stock_date_start=today - relativedelta(days=1),
|
|
stock_date_end=today - relativedelta(days=1),
|
|
stock_assign=True):
|
|
pbl = Product.products_by_location(
|
|
[storage.id], grouping_filter=([product.id],))
|
|
self.assertDictEqual(pbl, {})
|
|
|
|
with Transaction().set_context(
|
|
stock_date_start=today - relativedelta(days=1),
|
|
stock_date_end=today,
|
|
stock_assign=True):
|
|
pbl = Product.products_by_location(
|
|
[storage.id], grouping_filter=([product.id],))
|
|
self.assertDictEqual(pbl, {(storage.id, product.id): 5})
|
|
|
|
with Transaction().set_context(
|
|
stock_date_start=today,
|
|
stock_date_end=today,
|
|
stock_assign=True):
|
|
pbl = Product.products_by_location(
|
|
[storage.id], grouping_filter=([product.id],))
|
|
self.assertDictEqual(pbl, {(storage.id, product.id): 5})
|
|
|
|
with Transaction().set_context(
|
|
stock_date_start=today,
|
|
stock_date_end=today + relativedelta(days=1),
|
|
stock_assign=True):
|
|
pbl = Product.products_by_location(
|
|
[storage.id], grouping_filter=([product.id],))
|
|
self.assertDictEqual(pbl, {(storage.id, product.id): 5})
|
|
|
|
with Transaction().set_context(
|
|
stock_date_start=today + relativedelta(days=1),
|
|
stock_date_end=today + relativedelta(days=1),
|
|
stock_assign=True):
|
|
pbl = Product.products_by_location(
|
|
[storage.id], grouping_filter=([product.id],))
|
|
self.assertDictEqual(pbl, {})
|
|
|
|
@with_transaction()
|
|
def test_location_inactive_without_move(self):
|
|
"Test inactivate location without move"
|
|
pool = Pool()
|
|
Location = pool.get('stock.location')
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
location, = Location.create([{
|
|
'name': "Location",
|
|
'parent': storage.id,
|
|
}])
|
|
|
|
location.active = False
|
|
location.save()
|
|
|
|
@with_transaction()
|
|
def test_location_inactive_with_quantity(self):
|
|
"Test inactivate location with quantity"
|
|
pool = Pool()
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Uom = pool.get('product.uom')
|
|
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
location, = Location.create([{
|
|
'name': "Location",
|
|
'parent': storage.id,
|
|
}])
|
|
unit, = Uom.search([('name', '=', "Unit")])
|
|
template, = Template.create([{
|
|
'name': "Product",
|
|
'type': 'goods',
|
|
'default_uom': unit.id,
|
|
}])
|
|
product, = Product.create([{'template': template.id}])
|
|
|
|
company = create_company()
|
|
with set_company(company):
|
|
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': storage.id,
|
|
'to_location': location.id,
|
|
'company': company.id,
|
|
}])
|
|
Move.do(moves)
|
|
with self.assertRaises(LocationValidationError):
|
|
location.active = False
|
|
location.save()
|
|
|
|
@with_transaction()
|
|
def test_location_inactive_with_draft_moves(self):
|
|
"Test inactivate location with draft moves"
|
|
pool = Pool()
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Uom = pool.get('product.uom')
|
|
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
location, = Location.create([{
|
|
'name': "Location",
|
|
'parent': storage.id,
|
|
}])
|
|
unit, = Uom.search([('name', '=', "Unit")])
|
|
template, = Template.create([{
|
|
'name': "Product",
|
|
'type': 'goods',
|
|
'default_uom': unit.id,
|
|
}])
|
|
product, = Product.create([{'template': template.id}])
|
|
|
|
company = create_company()
|
|
with set_company(company):
|
|
Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': storage.id,
|
|
'to_location': location.id,
|
|
'company': company.id,
|
|
}, {
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': location.id,
|
|
'to_location': storage.id,
|
|
'company': company.id,
|
|
}])
|
|
with self.assertRaises(LocationValidationError):
|
|
location.active = False
|
|
location.save()
|
|
|
|
@with_transaction()
|
|
def test_inactive_products_with_stock(self):
|
|
"Test inactivate products with stock"
|
|
pool = Pool()
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Uom = pool.get('product.uom')
|
|
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
supplier, = Location.search([('code', '=', 'SUP')])
|
|
unit, = Uom.search([('name', '=', "Unit")])
|
|
template, = Template.create([{
|
|
'name': "Product",
|
|
'type': 'goods',
|
|
'default_uom': unit.id,
|
|
}])
|
|
products = Product.create([{'template': template.id}] * 2)
|
|
product, product2 = products
|
|
|
|
company = create_company()
|
|
with set_company(company):
|
|
Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'unit_price': Decimal('5'),
|
|
'currency': company.currency.id,
|
|
'company': company.id,
|
|
}, {
|
|
'product': product2.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'unit_price': Decimal('5'),
|
|
'currency': company.currency.id,
|
|
'company': company.id,
|
|
}])
|
|
with check_access():
|
|
with self.assertRaises(ProductStockWarning):
|
|
Product.write(products, {'active': False})
|
|
|
|
with self.assertRaises(ProductStockWarning):
|
|
Template.write([template], {'active': False})
|
|
|
|
Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': storage.id,
|
|
'to_location': supplier.id,
|
|
'unit_price': Decimal('5'),
|
|
'currency': company.currency.id,
|
|
'company': company.id,
|
|
}, {
|
|
'product': product2.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': storage.id,
|
|
'to_location': supplier.id,
|
|
'unit_price': Decimal('5'),
|
|
'currency': company.currency.id,
|
|
'company': company.id,
|
|
}])
|
|
with check_access():
|
|
Template.write([template], {'active': False})
|
|
Product.write(products, {'active': False})
|
|
|
|
@with_transaction()
|
|
def test_inactive_products_one_with_stock(self):
|
|
"Test inactivate products one with stock"
|
|
pool = Pool()
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Uom = pool.get('product.uom')
|
|
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
supplier, = Location.search([('code', '=', 'SUP')])
|
|
unit, = Uom.search([('name', '=', "Unit")])
|
|
template, = Template.create([{
|
|
'name': "Product",
|
|
'type': 'goods',
|
|
'default_uom': unit.id,
|
|
}])
|
|
products = Product.create([{'template': template.id}] * 2)
|
|
product, product2 = products
|
|
|
|
company = create_company()
|
|
with set_company(company):
|
|
Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': supplier.id,
|
|
'to_location': storage.id,
|
|
'unit_price': Decimal('5'),
|
|
'currency': company.currency.id,
|
|
'company': company.id,
|
|
}])
|
|
with check_access():
|
|
with self.assertRaises(ProductStockWarning):
|
|
Product.write(products, {'active': False})
|
|
|
|
with self.assertRaises(ProductStockWarning):
|
|
Template.write([template], {'active': False})
|
|
|
|
Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': storage.id,
|
|
'to_location': supplier.id,
|
|
'unit_price': Decimal('5'),
|
|
'currency': company.currency.id,
|
|
'company': company.id,
|
|
}])
|
|
with check_access():
|
|
Template.write([template], {'active': False})
|
|
Product.write(products, {'active': False})
|
|
|
|
@with_transaction()
|
|
def test_location_inactive_with_consumable_product(self):
|
|
"Test inactive location with consumable products"
|
|
pool = Pool()
|
|
Location = pool.get('stock.location')
|
|
Move = pool.get('stock.move')
|
|
Template = pool.get('product.template')
|
|
Product = pool.get('product.product')
|
|
Uom = pool.get('product.uom')
|
|
|
|
warehouse, = Location.search([('type', '=', 'warehouse')])
|
|
storage, = Location.search([('code', '=', 'STO')])
|
|
lost_found, = Location.search([('type', '=', 'lost_found')])
|
|
|
|
unit, = Uom.search([('name', '=', "Unit")])
|
|
template, = Template.create([{
|
|
'name': "Product",
|
|
'type': 'goods',
|
|
'default_uom': unit.id,
|
|
'consumable': True,
|
|
}])
|
|
product, = Product.create([{'template': template.id}])
|
|
|
|
company = create_company()
|
|
with set_company(company):
|
|
today = datetime.date.today()
|
|
|
|
moves = Move.create([{
|
|
'product': product.id,
|
|
'unit': unit.id,
|
|
'quantity': 1,
|
|
'from_location': lost_found.id,
|
|
'to_location': storage.id,
|
|
'planned_date': today,
|
|
'effective_date': today,
|
|
'company': company.id,
|
|
}])
|
|
Move.do(moves)
|
|
warehouse.active = False
|
|
warehouse.save()
|
|
|
|
|
|
del ModuleTestCase
|