Files
tryton/modules/product_cost_history/product.py
T

159 lines
6.0 KiB
Python
Raw Normal View History

2014-12-02 12:55:35 +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.
from sql import Column, Window
2013-09-06 22:34:27 +02:00
from sql.aggregate import Max
from sql.conditionals import Coalesce
from sql.functions import LastValue
2021-12-11 17:58:43 +01:00
2023-03-23 00:33:22 +01:00
from trytond.model import ModelSQL, ModelView, convert_from, fields
2021-12-11 17:58:43 +01:00
from trytond.modules.product import round_price
2017-04-01 00:32:16 +02:00
from trytond.pool import Pool, PoolMeta
2022-10-09 13:27:55 +02:00
from trytond.tools import timezone as tz
2020-01-26 22:09:09 +01:00
from trytond.transaction import Transaction
2017-04-01 00:32:16 +02:00
2020-01-26 22:09:09 +01:00
class Product(metaclass=PoolMeta):
__name__ = 'product.product'
def get_multivalue(self, name, **pattern):
2021-10-21 00:12:30 +02:00
pool = Pool()
Company = pool.get('company.company')
2020-01-26 22:09:09 +01:00
context = Transaction().context
if (name == 'cost_price'
and context.get('_datetime')
and self.type in ['goods', 'assets']):
2021-10-21 00:12:30 +02:00
datetime = context['_datetime']
company = pattern.get(
'company', Transaction().context.get('company'))
if company is not None and company >= 0:
2021-10-21 00:12:30 +02:00
company = Company(company)
if company.timezone:
2022-10-09 13:27:55 +02:00
timezone = tz.ZoneInfo(company.timezone)
try:
datetime = (
datetime.replace(tzinfo=tz.UTC).astimezone(
timezone))
except OverflowError:
pass
2021-10-21 00:12:30 +02:00
cost_price = self.get_cost_price_at(datetime.date(), **pattern)
2020-01-26 22:09:09 +01:00
if cost_price is not None:
return cost_price
return super().get_multivalue(name, **pattern)
def get_cost_price_at(self, date, **pattern):
2020-01-26 22:09:09 +01:00
pool = Pool()
CostHistory = pool.get('product.product.cost_history')
company = pattern.get(
'company', Transaction().context.get('company'))
with Transaction().set_context(company=company):
records = CostHistory.search([
('date', '<=', date),
('product', '=', self.id),
], limit=1, order=[('date', 'DESC')])
2020-01-26 22:09:09 +01:00
if records:
record, = records
2020-04-23 21:44:00 +02:00
return round_price(record.cost_price)
2020-01-26 22:09:09 +01:00
class CostPrice(metaclass=PoolMeta):
2017-04-01 00:32:16 +02:00
__name__ = 'product.cost_price'
_history = True
2012-09-24 22:18:02 +02:00
class ProductCostHistory(ModelSQL, ModelView):
2012-09-24 22:18:02 +02:00
__name__ = 'product.product.cost_history'
product = fields.Many2One('product.product', "Product")
2020-01-26 22:09:09 +01:00
date = fields.Date('Date')
cost_price = fields.Numeric('Cost Price')
2012-09-24 22:18:02 +02:00
@classmethod
def __setup__(cls):
2025-04-04 00:45:18 +02:00
super().__setup__()
2012-09-24 22:18:02 +02:00
cls._order.insert(0, ('date', 'DESC'))
2013-09-06 22:34:27 +02:00
@classmethod
def table_query(cls):
2012-09-24 22:18:02 +02:00
pool = Pool()
2020-01-26 22:09:09 +01:00
Move = pool.get('stock.move')
Product = pool.get('product.product')
Template = pool.get('product.template')
CostPrice = pool.get('product.cost_price')
2021-10-21 00:12:30 +02:00
User = pool.get('res.user')
2020-01-26 22:09:09 +01:00
move = Move.__table__()
product = Product.__table__()
template = Template.__table__()
history = CostPrice.__table_history__()
2021-10-21 00:12:30 +02:00
transaction = Transaction()
user = User(transaction.user)
2020-01-26 22:09:09 +01:00
tables, clause = Move.search_domain([
('cost_price', '!=', None),
2020-01-26 22:09:09 +01:00
('state', '=', 'done'),
Product._domain_moves_cost(),
['OR',
Product._domain_in_moves_cost(),
Product._domain_out_moves_cost(),
],
2020-01-26 22:09:09 +01:00
], tables={
None: (move, None),
})
cost_price = Coalesce(move.product_cost_price, move.cost_price)
2025-10-04 22:45:53 +02:00
window = Window(
[move.effective_date, move.product],
frame='ROWS', start=None, end=None,
order_by=[move.write_date.asc, move.id.asc])
cost_price = LastValue(cost_price, window=window)
2020-01-26 22:09:09 +01:00
2023-03-23 00:33:22 +01:00
move_history = convert_from(None, tables, type_='INNER').select(
2020-01-26 22:09:09 +01:00
(move.id * 2).as_('id'),
move.effective_date.as_('date'),
move.product.as_('product'),
cost_price.as_('cost_price'),
where=clause)
query = move_history.select(
Max(move_history.id).as_('id'),
move_history.date.as_('date'),
move_history.product.as_('product'),
Max(move_history.cost_price).as_('cost_price'),
group_by=[move_history.date, move_history.product])
2021-10-21 00:12:30 +02:00
if user.company:
timezone = user.company.timezone
else:
timezone = None
2020-01-26 22:09:09 +01:00
price_datetime = Coalesce(history.write_date, history.create_date)
2021-10-21 00:12:30 +02:00
price_date = cls.date.sql_cast(price_datetime, timezone=timezone)
2025-10-04 22:45:53 +02:00
window = Window(
[price_date, history.product],
frame='ROWS', start=None, end=None,
order_by=[price_datetime.asc])
cost_price = LastValue(history.cost_price, window=window)
2020-01-26 22:09:09 +01:00
price_history = (history
.join(product, condition=history.product == product.id)
.join(template, condition=product.template == template.id)
.select(
(Column(history, '__id') * 2 + 1).as_('id'),
price_date.as_('date'),
history.product.as_('product'),
cost_price.as_('cost_price'),
where=~template.type.in_(['goods', 'assets'])
2021-10-21 00:12:30 +02:00
& cls._non_moves_clause(history, user)))
2020-01-26 22:09:09 +01:00
query |= price_history.select(
Max(price_history.id).as_('id'),
price_history.date.as_('date'),
price_history.product.as_('product'),
Max(price_history.cost_price).as_('cost_price'),
group_by=[price_history.date, price_history.product])
return query
@classmethod
2021-10-21 00:12:30 +02:00
def _non_moves_clause(cls, history_table, user):
company_id = user.company.id if user.company else None
return history_table.company == company_id
2017-07-26 23:43:23 +02:00
def get_rec_name(self, name):
return Pool().get('ir.lang').get().strftime(self.date)