===============================
Stock Reporting Margin Scenario
===============================

Imports::

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

    >>> from proteus import Model
    >>> 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

    >>> today = dt.date.today()
    >>> yesterday = today - dt.timedelta(days=1)

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)

Get currency::

    >>> currency = get_currency()

Create product::

    >>> ProductUom = Model.get('product.uom')
    >>> ProductTemplate = Model.get('product.template')
    >>> unit, = ProductUom.find([('name', '=', 'Unit')])
    >>> template = ProductTemplate()
    >>> template.name = 'Product'
    >>> template.default_uom = unit
    >>> template.type = 'goods'
    >>> template.list_price = Decimal('40')
    >>> template.save()
    >>> product, = template.products
    >>> product.cost_price = Decimal('20')
    >>> product.save()
    >>> template2, = template.duplicate()
    >>> product2, = template2.products

    >>> Category = Model.get('product.category')
    >>> category_root = Category(name="Root")
    >>> category_root.save()
    >>> category1 = Category(name="Child1", parent=category_root)
    >>> category1.save()
    >>> category2 = Category(name="Child2", parent=category_root)
    >>> category2.save()

    >>> template.categories.append(Category(category1.id))
    >>> template.save()
    >>> template2.categories.append(Category(category2.id))
    >>> template2.save()


Get stock locations::

    >>> Location = Model.get('stock.location')
    >>> supplier_loc, = Location.find([('code', '=', 'SUP')])
    >>> customer_loc, = Location.find([('code', '=', 'CUS')])
    >>> storage_loc, = Location.find([('code', '=', 'STO')])
    >>> lost_loc, = Location.find([('type', '=', 'lost_found')])

Create some moves::

    >>> Move = Model.get('stock.move')
    >>> move = Move()
    >>> move.product = product
    >>> move.from_location = supplier_loc
    >>> move.to_location = storage_loc
    >>> move.quantity = 8
    >>> move.unit_price = Decimal('20')
    >>> move.currency = currency
    >>> move.effective_date = yesterday
    >>> move.click('do')

    >>> move = Move()
    >>> move.product = product
    >>> move.from_location = storage_loc
    >>> move.to_location = customer_loc
    >>> move.quantity = 2
    >>> move.unit_price = Decimal('40')
    >>> move.currency = currency
    >>> move.effective_date = yesterday
    >>> move.click('do')

    >>> move = Move()
    >>> move.product = product
    >>> move.from_location = storage_loc
    >>> move.to_location = customer_loc
    >>> move.quantity = 4
    >>> move.unit_price = Decimal('30')
    >>> move.currency = currency
    >>> move.effective_date = today
    >>> move.click('do')

    >>> move = Move()
    >>> move.product = product
    >>> move.from_location = customer_loc
    >>> move.to_location = storage_loc
    >>> move.quantity = 1
    >>> move.unit_price = Decimal('30')
    >>> move.currency = currency
    >>> move.effective_date = today
    >>> move.click('do')

    >>> move = Move()
    >>> move.product = product2
    >>> move.from_location = storage_loc
    >>> move.to_location = customer_loc
    >>> move.quantity = 2
    >>> move.unit_price = Decimal('50')
    >>> move.currency = currency
    >>> move.effective_date = today
    >>> move.click('do')

    >>> move = Move()
    >>> move.product = product
    >>> move.from_location = storage_loc
    >>> move.to_location = lost_loc
    >>> move.quantity = 1
    >>> move.effective_date = today
    >>> move.click('do')

Check reporting margin per product::

    >>> MarginProduct = Model.get('stock.reporting.margin.product')
    >>> MarginProductTimeseries = Model.get(
    ...     'stock.reporting.margin.product.time_series')
    >>> context = {
    ...     'from_date': yesterday,
    ...     'to_date': today,
    ...     'period': 'day',
    ...     }
    >>> with config.set_context(context=context):
    ...     reports = MarginProduct.find([])
    ...     time_series = MarginProductTimeseries.find([])
    >>> len(reports)
    2
    >>> report, = [r for r in reports if r.product == product]
    >>> (report.quantity, report.cost, report.revenue, report.profit, report.margin)
    (5.0, Decimal('100.00'), Decimal('170.00'), Decimal('70.00'), Decimal('0.4118'))
    >>> len(time_series)
    3
    >>> with config.set_context(context=context):
    ...     assertEqual({(
    ...             r.product.id, str(r.date), r.quantity, r.cost, r.revenue,
    ...             r.profit, r.margin)
    ...         for r in time_series},
    ...         {
    ...             (product.id, str(yesterday), 2, Decimal('40.00'),
    ...                 Decimal('80.00'), Decimal('40.00'), Decimal('0.5000')),
    ...             (product.id, str(today), 3, Decimal('60.00'),
    ...                 Decimal('90.00'), Decimal('30.00'), Decimal('0.3333')),
    ...             (product2.id, str(today), 2, Decimal('40.00'),
    ...                 Decimal('100.00'), Decimal('60.00'), Decimal('0.6000'))})

Check reporting margin per categories::

    >>> MarginCategory = Model.get('stock.reporting.margin.category')
    >>> MarginCategoryTimeseries = Model.get(
    ...     'stock.reporting.margin.category.time_series')
    >>> MarginCategoryTree = Model.get(
    ...     'stock.reporting.margin.category.tree')
    >>> with config.set_context(context=context):
    ...     reports = MarginCategory.find([])
    ...     time_series = MarginCategoryTimeseries.find([])
    ...     tree = MarginCategoryTree.find([])
    >>> len(reports)
    2
    >>> with config.set_context(context=context):
    ...     assertEqual({(r.category.id, r.cost, r.revenue, r.profit, r.margin)
    ...         for r in reports},
    ...     {(category1.id, Decimal('100.00'), Decimal('170.00'),
    ...         Decimal('70.00'), Decimal('0.4118')),
    ...     (category2.id, Decimal('40.00'), Decimal('100.00'),
    ...         Decimal('60.00'), Decimal('0.6000'))})
    >>> len(time_series)
    3
    >>> with config.set_context(context=context):
    ...     assertEqual({
    ...             (r.category.id, str(r.date), r.cost, r.revenue, r.profit, r.margin)
    ...             for r in time_series},
    ...         {
    ...             (category1.id, str(yesterday), Decimal('40.00'), Decimal('80.00'),
    ...                 Decimal('40.00'), Decimal('0.5000')),
    ...             (category1.id, str(today), Decimal('60.00'), Decimal('90.00'),
    ...                 Decimal('30.00'), Decimal('0.3333')),
    ...             (category2.id, str(today), Decimal('40.00'), Decimal('100.00'),
    ...                 Decimal('60.00'), Decimal('0.6000'))})
    >>> len(tree)
    3
    >>> with config.set_context(context=context):
    ...     assertEqual({(r.name, r.cost, r.revenue, r.profit, r.margin)
    ...         for r in tree},
    ...     {("Root", Decimal('140.00'), Decimal('270.00'),
    ...         Decimal('130.00'), Decimal('0.4815')),
    ...     ("Child1", Decimal('100.00'), Decimal('170.00'),
    ...         Decimal('70.00'), Decimal('0.4118')),
    ...     ('Child2', Decimal('40.00'), Decimal('100.00'),
    ...         Decimal('60.00'), Decimal('0.6000'))})
    >>> child1, = MarginCategoryTree.find([('rec_name', '=', 'Child1')])
    >>> child1.rec_name
    'Child1'

Check reporting margin including lost::

    >>> context['include_lost'] = True

    >>> with config.set_context(context=context):
    ...     reports = MarginProduct.find([])
    >>> len(reports)
    2
    >>> report, = [r for r in reports if r.product == product]
    >>> (report.quantity, report.cost, report.revenue, report.profit, report.margin)
    (6.0, Decimal('120.00'), Decimal('170.00'), Decimal('50.00'), Decimal('0.2941'))
