Files
2026-03-14 09:42:12 +00:00

268 lines
9.9 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.
from trytond.model import ModelSQL, fields
from trytond.modules.company.model import CompanyValueMixin
from trytond.modules.product.product import TemplateFunction
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval
from trytond.transaction import Transaction
cost_price_warehouse = fields.Boolean(
"Cost Price per Warehouse",
help="Compute product cost price for each warehouse.")
class Configuration(metaclass=PoolMeta):
__name__ = 'product.configuration'
cost_price_warehouse = fields.MultiValue(cost_price_warehouse)
@classmethod
def default_cost_price_warehouse(cls, **pattern):
return cls.multivalue_model(
'cost_price_warehouse').default_cost_price_warehouse()
class ConfigurationCostPriceWarehouse(ModelSQL, CompanyValueMixin):
__name__ = 'product.configuration.cost_price_warehouse'
cost_price_warehouse = cost_price_warehouse
@classmethod
def default_cost_price_warehouse(cls):
return False
class Product(metaclass=PoolMeta):
__name__ = 'product.product'
@classmethod
def __setup__(cls):
super().__setup__()
readonly = (
Eval('context', {}).get('cost_price_warehouse', False)
& ~Eval('context', {}).get('warehouse'))
if cls.cost_price.states['readonly']:
cls.cost_price.states['readonly'] |= readonly
else:
cls.cost_price.states['readonly'] = readonly
def multivalue_records(self, field):
Value = self.multivalue_model(field)
records = super().multivalue_records(field)
if issubclass(Value, CostPrice):
# Sort to get record with empty warehouse at the end
records = sorted(records, key=lambda r: r.warehouse is None)
return records
def get_multivalue(self, name, **pattern):
pool = Pool()
Company = pool.get('company.company')
context = Transaction().context
if not isinstance(self._fields[name], TemplateFunction):
Value = self.multivalue_model(name)
if issubclass(Value, CostPrice) and context.get('company'):
company = Company(context['company'])
if company.cost_price_warehouse:
pattern.setdefault('warehouse', context.get('warehouse'))
return super().get_multivalue(name, **pattern)
def set_multivalue(self, name, value, save=True, **pattern):
pool = Pool()
Company = pool.get('company.company')
context = Transaction().context
Value = self.multivalue_model(name)
if issubclass(Value, CostPrice) and context.get('company'):
company = Company(context['company'])
if company.cost_price_warehouse:
pattern.setdefault('warehouse', context.get('warehouse'))
if not value and not pattern.get('warehouse'):
return []
return super().set_multivalue(name, value, save=save, **pattern)
@classmethod
def _domain_moves_cost(cls):
pool = Pool()
Company = pool.get('company.company')
context = Transaction().context
domain = super()._domain_moves_cost()
if context.get('company'):
company = Company(context['company'])
if company.cost_price_warehouse:
warehouse = context.get('warehouse')
domain = [
domain,
['OR',
('from_location', 'child_of', warehouse, 'parent'),
('from_location.cost_warehouse', '=', warehouse),
('to_location', 'child_of', warehouse, 'parent'),
('to_location.cost_warehouse', '=', warehouse),
],
]
return domain
@classmethod
def _domain_in_moves_cost(cls):
pool = Pool()
Company = pool.get('company.company')
context = Transaction().context
domain = super()._domain_in_moves_cost()
if context.get('company'):
company = Company(context['company'])
if company.cost_price_warehouse:
warehouse = context.get('warehouse')
domain = ['OR',
domain,
[
('from_location.type', '=', 'storage'),
('to_location.type', '=', 'storage'),
('from_location', 'not child_of', warehouse, 'parent'),
['OR',
('from_location.cost_warehouse', '!=', warehouse),
('from_location.cost_warehouse', '=', None),
],
['OR',
('to_location', 'child_of', warehouse, 'parent'),
('to_location.cost_warehouse', '=', warehouse),
],
]
]
return domain
@classmethod
def _domain_out_moves_cost(cls):
pool = Pool()
Company = pool.get('company.company')
context = Transaction().context
domain = super()._domain_out_moves_cost()
if context.get('company'):
company = Company(context['company'])
if company.cost_price_warehouse:
warehouse = context.get('warehouse')
domain = ['OR',
domain,
[
('from_location.type', '=', 'storage'),
('to_location.type', '=', 'storage'),
('to_location', 'not child_of', warehouse, 'parent'),
['OR',
('to_location.cost_warehouse', '!=', warehouse),
('to_location.cost_warehouse', '=', None),
],
['OR',
('from_location', 'child_of', warehouse, 'parent'),
('from_location.cost_warehouse', '=', warehouse),
],
]
]
return domain
@classmethod
def _domain_storage_quantity(cls):
pool = Pool()
Company = pool.get('company.company')
context = Transaction().context
domain = super()._domain_storage_quantity()
if context.get('company'):
company = Company(context['company'])
if company.cost_price_warehouse:
warehouse = context.get('warehouse')
domain = [
domain,
['OR',
('parent', 'child_of', warehouse),
('cost_warehouse', '=', warehouse),
],
]
return domain
@classmethod
def default_cost_price(cls, **pattern):
context = Transaction().context
default = super().default_cost_price()
if (context.get('cost_price_warehouse')
and not pattern.get('warehouse', context.get('warehouse'))):
default = None
return default
class CostPrice(metaclass=PoolMeta):
__name__ = 'product.cost_price'
warehouse = fields.Many2One(
'stock.location', "Warehouse", ondelete='RESTRICT',
domain=[
('type', '=', 'warehouse'),
],
states={
'required': Eval('warehouse_required', False),
})
warehouse_required = fields.Function(fields.Boolean(
"Warehouse Required"), 'get_warehouse_required')
@classmethod
def get_warehouse_required(cls, records, name):
return {r.id: r.company.cost_price_warehouse for r in records}
class CostPriceRevision(metaclass=PoolMeta):
__name__ = 'product.cost_price.revision'
warehouse = fields.Many2One(
'stock.location', "Warehouse", ondelete='RESTRICT',
domain=[
('type', '=', 'warehouse'),
],
states={
'required': Eval('warehouse_required', False),
})
warehouse_required = fields.Function(fields.Boolean(
"Warehouse Required"), 'get_warehouse_required')
@classmethod
def default_warehouse(cls):
pool = Pool()
Company = pool.get('company.company')
company_id = cls.default_company()
if company_id is not None and company_id >= 0:
company = Company(company_id)
if company.cost_price_warehouse:
return Transaction().context.get('warehouse')
@classmethod
def get_warehouse_required(cls, records, name):
return {r.id: r.company.cost_price_warehouse for r in records}
@classmethod
def _get_for_product_domain(cls):
pool = Pool()
Company = pool.get('company.company')
domain = super()._get_for_product_domain()
context = Transaction().context
if context.get('company'):
company = Company(context['company'])
if company.cost_price_warehouse:
domain = [
domain,
('warehouse', '=', context.get('warehouse')),
]
return domain
class ModifyCostPrice(metaclass=PoolMeta):
__name__ = 'product.modify_cost_price'
def get_revision(self, Revision):
revision = super().get_revision(Revision)
if revision.company.cost_price_warehouse:
revision.warehouse = Transaction().context.get('warehouse')
return revision
class ProductCostHistory(metaclass=PoolMeta):
__name__ = 'product.product.cost_history'
@classmethod
def _non_moves_clause(cls, history_table, user):
clause = super()._non_moves_clause(history_table, user)
if user.company and user.company.cost_price_warehouse:
warehouse_id = user.warehouse.id if user.warehouse else None
clause &= history_table.warehouse == warehouse_id
return clause