diff --git a/modules/sale/CHANGELOG b/modules/sale/CHANGELOG
index 1a82032875..13b53a7834 100644
--- a/modules/sale/CHANGELOG
+++ b/modules/sale/CHANGELOG
@@ -1,3 +1,4 @@
+* Add relate to open products from sale
Version 7.4.0 - 2024-11-04
--------------------------
diff --git a/modules/sale/__init__.py b/modules/sale/__init__.py
index 84901cfb41..7f8f812fa2 100644
--- a/modules/sale/__init__.py
+++ b/modules/sale/__init__.py
@@ -61,6 +61,7 @@ def register():
sale.HandleInvoiceException,
sale.ReturnSale,
sale.ModifyHeader,
+ sale.OpenProduct,
party.Replace,
party.Erase,
sale_reporting.OpenRegionTree,
diff --git a/modules/sale/product.py b/modules/sale/product.py
index cc172a56e2..ca4952f4d8 100644
--- a/modules/sale/product.py
+++ b/modules/sale/product.py
@@ -242,10 +242,13 @@ class SaleContext(ModelView):
def default_locations(cls):
pool = Pool()
Location = pool.get('stock.location')
- locations = []
- warehouse = Location.get_default_warehouse()
- if warehouse:
- locations.append(warehouse)
+ context = Transaction().context
+ locations = context.get('locations')
+ if locations is None:
+ locations = []
+ warehouse = Location.get_default_warehouse()
+ if warehouse:
+ locations.append(warehouse)
return locations
@classmethod
@@ -256,10 +259,26 @@ class SaleContext(ModelView):
def default_currency(cls):
pool = Pool()
Company = pool.get('company.company')
- company_id = cls.default_company()
- if company_id:
- company = Company(company_id)
- return company.currency.id
+ context = Transaction().context
+ currency = context.get('currency')
+ if currency is None:
+ company_id = cls.default_company()
+ if company_id:
+ company = Company(company_id)
+ currency = company.currency.id
+ return currency
+
+ @classmethod
+ def default_customer(cls):
+ return Transaction().context.get('customer')
+
+ @classmethod
+ def default_sale_date(cls):
+ return Transaction().context.get('sale_date')
+
+ @classmethod
+ def default_quantity(cls):
+ return Transaction().context.get('quantity')
@fields.depends('sale_date')
def on_change_with_stock_date_end(self, name=None):
diff --git a/modules/sale/sale.py b/modules/sale/sale.py
index 6da2e43a7b..388f924ca2 100644
--- a/modules/sale/sale.py
+++ b/modules/sale/sale.py
@@ -25,7 +25,7 @@ from trytond.modules.company.model import (
from trytond.modules.currency.fields import Monetary
from trytond.modules.product import price_digits
from trytond.pool import Pool
-from trytond.pyson import Bool, Eval, If
+from trytond.pyson import Bool, Eval, If, PYSONEncoder
from trytond.tools import cached_property, firstline, sortable_values
from trytond.transaction import Transaction
from trytond.wizard import (
@@ -2467,3 +2467,50 @@ class ModifyHeader(Wizard):
Line.save(sale.lines)
return 'end'
+
+
+class OpenProduct(Wizard):
+ __name__ = 'sale.open_product'
+
+ start = StateAction('sale.act_product')
+
+ def _context(self):
+ context = {
+ 'stock_skip_warehouse': True,
+ 'locations': [],
+ }
+ companies = set()
+ currencies = set()
+ customers = set()
+ sale_dates = set()
+ for record in self.records:
+ companies.add(record.company.id)
+ currencies.add(record.currency.id)
+ customers.add(record.party.id)
+ sale_dates.add(record.sale_date)
+ if self.record.warehouse:
+ context['locations'].append(self.record.warehouse.id)
+ if len(companies) == 1:
+ context['company'], = companies
+ if len(currencies) == 1:
+ context['currency'], = currencies
+ if len(customers) == 1:
+ context['customer'], = customers
+ if len(sale_dates) == 1:
+ context['sale_date'], = sale_dates
+ return context
+
+ def do_start(self, action):
+ product_ids = {
+ l.product.id for r in self.records for l in r.lines if l.product}
+ encoder = PYSONEncoder()
+ domain = [
+ ('id', 'in', list(product_ids)),
+ ]
+ action['pyson_domain'] = encoder.encode(domain)
+ action['pyson_context'] = encoder.encode(self._context())
+ name_suffix = ', '.join(r.rec_name for r in self.records[:5])
+ if len(self.records) > 5:
+ name_suffix += ',...'
+ action['name'] += f' ({name_suffix})'
+ return action, {}
diff --git a/modules/sale/sale.xml b/modules/sale/sale.xml
index b53541603b..559a8c0332 100644
--- a/modules/sale/sale.xml
+++ b/modules/sale/sale.xml
@@ -542,5 +542,16 @@ this repository contains the full copyright notices and license terms. -->
eval="Eval('context', {}).get('modify_header', False)"
pyson="1"/>
+
+
+ Products
+ sale.open_product
+ sale.sale
+
+
+ form_relate
+ sale.sale,-1
+
+
diff --git a/modules/sale_price_list/__init__.py b/modules/sale_price_list/__init__.py
index ad0a00bc0c..cec58797d2 100644
--- a/modules/sale_price_list/__init__.py
+++ b/modules/sale_price_list/__init__.py
@@ -18,3 +18,6 @@ def register():
configuration.Configuration,
configuration.ConfigurationSalePriceList,
module='sale_price_list', type_='model')
+ Pool.register(
+ sale.OpenProduct,
+ module='sale_price_list', type_='wizard')
diff --git a/modules/sale_price_list/product.py b/modules/sale_price_list/product.py
index a8ec55f3f2..fa8f8c56b6 100644
--- a/modules/sale_price_list/product.py
+++ b/modules/sale_price_list/product.py
@@ -63,10 +63,14 @@ class SaleContext(metaclass=PoolMeta):
def default_price_list(cls, **pattern):
pool = Pool()
Configuration = pool.get('sale.configuration')
- config = Configuration(1)
- price_list = config.get_multivalue('sale_price_list', **pattern)
- if price_list:
- return price_list.id
+ context = Transaction().context
+ price_list = context.get('price_list')
+ if price_list is None:
+ config = Configuration(1)
+ price_list = config.get_multivalue('sale_price_list', **pattern)
+ if price_list:
+ price_list = price_list.id
+ return price_list
@fields.depends(methods=['on_change_customer'])
def on_change_company(self):
diff --git a/modules/sale_price_list/sale.py b/modules/sale_price_list/sale.py
index 614487cb04..aadd048380 100644
--- a/modules/sale_price_list/sale.py
+++ b/modules/sale_price_list/sale.py
@@ -66,3 +66,16 @@ class Line(metaclass=PoolMeta):
if getattr(self.sale, 'price_list', None):
context['price_list'] = self.sale.price_list.id
return context
+
+
+class OpenProduct(metaclass=PoolMeta):
+ __name__ = 'sale.open_product'
+
+ def _context(self):
+ context = super()._context()
+ price_lists = {r.price_list for r in self.records}
+ if len(price_lists) == 1:
+ price_list, = price_lists
+ if price_list:
+ context['price_list'] = price_list.id
+ return context