==================================
Stock Reporting Inventory Scenario
==================================

Imports::

    >>> import datetime as dt
    >>> from decimal import Decimal
    >>> from unittest.mock import patch

    >>> from proteus import Model
    >>> from trytond.ir.date import Date
    >>> from trytond.modules.company.tests.tools import create_company
    >>> from trytond.modules.currency.tests.tools import get_currency
    >>> from trytond.modules.stock.move import Move
    >>> from trytond.tests.tools import activate_modules, assertEqual

    >>> period_close = globals().get('period_close', False)
    >>> product_type = globals().get('product_type', 'product.template')

Patch today::

    >>> mock = patch.object(Date, 'today', return_value=dt.date(2025, 1, 15))
    >>> _ = mock.start()

Patch on_change_with_assignation_required::

    >>> _ = patch.object(
    ...     Move, 'on_change_with_assignation_required',
    ...     return_value=False).start()

Activate modules::

    >>> config = activate_modules('stock', create_company)

    >>> Inventory = Model.get('stock.reporting.inventory')
    >>> InventoryMove = Model.get('stock.reporting.inventory.move')
    >>> InventoryDaily = Model.get('stock.reporting.inventory.daily')
    >>> InventoryTurnover = Model.get('stock.reporting.inventory.turnover')
    >>> Location = Model.get('stock.location')
    >>> Move = Model.get('stock.move')
    >>> Period = Model.get('stock.period')
    >>> ProductTemplate = Model.get('product.template')
    >>> ProductUom = Model.get('product.uom')

Get currency::

    >>> currency = get_currency()

Create product::

    >>> unit, = ProductUom.find([('name', '=', 'Unit')])

    >>> template = ProductTemplate()
    >>> template.name = 'Product'
    >>> template.default_uom = unit
    >>> template.type = 'goods'
    >>> template.save()
    >>> product, = template.products

Get stock locations::

    >>> warehouse_loc, = Location.find([('code', '=', 'WH')])
    >>> supplier_loc, = Location.find([('code', '=', 'SUP')])
    >>> customer_loc, = Location.find([('code', '=', 'CUS')])
    >>> storage_loc, = Location.find([('code', '=', 'STO')])

Fill warehouse::

    >>> move = Move()
    >>> move.product = product
    >>> move.from_location = supplier_loc
    >>> move.to_location = storage_loc
    >>> move.quantity = 10
    >>> move.effective_date = dt.date(2025, 1, 1)
    >>> move.unit_price = Decimal('10.0000')
    >>> move.currency = currency
    >>> move.click('do')
    >>> move.state
    'done'

    >>> move = Move()
    >>> move.product = product
    >>> move.from_location = storage_loc
    >>> move.to_location = customer_loc
    >>> move.unit = unit
    >>> move.quantity = 1
    >>> move.effective_date = dt.date(2025, 1, 1)
    >>> move.unit_price = Decimal('20.0000')
    >>> move.currency = currency
    >>> move.click('do')
    >>> move.state
    'done'

    >>> move = Move()
    >>> move.product = product
    >>> move.from_location = storage_loc
    >>> move.to_location = customer_loc
    >>> move.unit = unit
    >>> move.quantity = 2
    >>> move.effective_date = dt.date(2025, 1, 10)
    >>> move.unit_price = Decimal('20.0000')
    >>> move.currency = currency
    >>> move.click('do')
    >>> move.state
    'done'

Forecast some moves::

    >>> move = Move()
    >>> move.product = product
    >>> move.from_location = supplier_loc
    >>> move.to_location = storage_loc
    >>> move.quantity = 10
    >>> move.effective_date = dt.date(2025, 1, 5)
    >>> move.unit_price = Decimal('10.0000')
    >>> move.currency = currency
    >>> move.save()
    >>> move.state
    'draft'

    >>> move = Move()
    >>> move.product = product
    >>> move.from_location = storage_loc
    >>> move.to_location = customer_loc
    >>> move.unit = unit
    >>> move.quantity = 3
    >>> move.planned_date = dt.date(2025, 1, 20)
    >>> move.unit_price = Decimal('20.0000')
    >>> move.currency = currency
    >>> move.save()
    >>> move.state
    'draft'

Close period::

    >>> period = Period(date=dt.date(2025, 1, 5))
    >>> if period_close:
    ...     period.click('close')


Check inventory::

    >>> with config.set_context(
    ...         location=warehouse_loc.id,
    ...         product_type=product_type,
    ...         date=dt.date(2024, 1, 1)):
    ...     Inventory.find([])
    []

    >>> with config.set_context(
    ...         location=warehouse_loc.id,
    ...         product_type=product_type,
    ...         date=dt.date(2025, 1, 1)):
    ...     inventory, = Inventory.find([])
    >>> inventory.quantity
    9.0
    >>> assertEqual(inventory.product_reference.__class__.__name__, product_type)

    >>> with config.set_context(
    ...         location=warehouse_loc.id,
    ...         product_type=product_type,
    ...         date=dt.date(2025, 1, 15)):
    ...     inventory, = Inventory.find([])
    >>> inventory.quantity
    7.0

    >>> with config.set_context(
    ...         location=warehouse_loc.id,
    ...         product_type=product_type,
    ...         date=dt.date(2025, 1, 20)):
    ...     inventory, = Inventory.find([])
    >>> inventory.quantity
    4.0

    >>> with config.set_context(
    ...         location=warehouse_loc.id,
    ...         product_type=product_type):
    ...     inventory_moves = InventoryMove.find([])
    ...     inventories_daily = InventoryDaily.find([])

    >>> [i.quantity for i in inventory_moves]
    [4.0, 7.0, 9.0, 10.0]
    >>> [(i.input_quantity, i.output_quantity) for i in inventory_moves]
    [(None, 3.0), (None, 2.0), (None, 1.0), (10.0, None)]

    >>> [i.quantity for i in inventories_daily]
    [4.0, 7.0, 9.0]
    >>> [(i.input_quantity, i.output_quantity) for i in inventories_daily]
    [(None, 3.0), (None, 2.0), (10.0, 1.0)]

    >>> with config.set_context(
    ...         location=warehouse_loc.id,
    ...         from_date=dt.date(2025, 1, 15),
    ...         to_date=dt.date(2025, 1, 29),
    ...         product_type=product_type):
    ...     inventory_moves = InventoryMove.find([])
    ...     inventories_daily = InventoryDaily.find([])

    >>> [i.quantity for i in inventory_moves]
    [4.0, 7.0]
    >>> [(i.input_quantity, i.output_quantity) for i in inventory_moves]
    [(None, 3.0), (None, None)]

    >>> [i.quantity for i in inventories_daily]
    [4.0, 7.0]
    >>> [(i.input_quantity, i.output_quantity) for i in inventories_daily]
    [(None, 3.0), (None, None)]

    >>> with config.set_context(
    ...         location=warehouse_loc.id,
    ...         from_date=dt.date(2025, 1, 10),
    ...         to_date=dt.date(2025, 1, 29),
    ...         product_type=product_type):
    ...     inventory_moves = InventoryMove.find([])
    ...     inventories_daily = InventoryDaily.find([])

    >>> assertEqual(
    ...     [i.quantity for i in inventory_moves],
    ...     [4.0, 7.0] if period_close else [4.0, 7.0, 9.0])
    >>> assertEqual(
    ...     [(i.input_quantity, i.output_quantity) for i in inventory_moves],
    ...     [(None, 3.0), (None, 2.0)] if period_close
    ...     else [(None, 3.0), (None, 2.0), (None, None)])

    >>> [i.quantity for i in inventories_daily]
    [4.0, 7.0]
    >>> [(i.input_quantity, i.output_quantity) for i in inventories_daily]
    [(None, 3.0), (None, 2.0)]

Check Inventory turnover::

    >>> with config.set_context(
    ...         location=warehouse_loc.id,
    ...         from_date=dt.date(2025, 1, 10),
    ...         to_date=dt.date(2025, 1, 29),
    ...         product_type=product_type):
    ...     turnover, = InventoryTurnover.find([])

    >>> turnover.output_quantity
    0.25
    >>> turnover.average_quantity
    5.5
    >>> turnover.turnover
    0.045
    >>> assertEqual(turnover.product_reference.__class__.__name__, product_type)
