diff --git a/modules/purchase_trade/docs/business/sessions.md b/modules/purchase_trade/docs/business/sessions.md
index bdfe469..e6f7aab 100644
--- a/modules/purchase_trade/docs/business/sessions.md
+++ b/modules/purchase_trade/docs/business/sessions.md
@@ -31,6 +31,8 @@ canonique seulement quand elle est reprise dans une page thematique.
Les contrats Sale et Purchase ont un onglet Charter Conditions au niveau entete et ligne. Une ligne sans conditions propres herite des conditions de l'entete; des conditions de ligne remplacent l'heritage.
+
Les conditions creees depuis une Sale ou une Sale Line sont limitees au role Customer; celles creees depuis une Purchase ou Purchase Line sont limitees au role Supplier. Les conditions creees depuis une Charter Party ne peuvent pas utiliser les roles Supplier ou Customer afin d'eviter la confusion entre conditions armateur/charte et conditions contractuelles achat/vente.
+
Les shipments et SOF exposent les conditions Owner, Supplier et Customer retrouvees depuis le charter party et les lots/lignes physiques.
Le calcul SOF privilegie la condition appliquee: laytime allowed, turn time, pumping rate, rate demurrage et rate despatch. A defaut, il garde les anciens champs legacy demurrage/pumping.
diff --git a/modules/purchase_trade/docs_source/business/sessions.md b/modules/purchase_trade/docs_source/business/sessions.md
index e5882d3..5159e8d 100644
--- a/modules/purchase_trade/docs_source/business/sessions.md
+++ b/modules/purchase_trade/docs_source/business/sessions.md
@@ -23,6 +23,12 @@ canonique seulement quand elle est reprise dans une page thematique.
- Les contrats Sale et Purchase ont un onglet `Charter Conditions` au
niveau entete et ligne. Une ligne sans conditions propres herite des
conditions de l'entete; des conditions de ligne remplacent l'heritage.
+ - Les conditions creees depuis une Sale ou une Sale Line sont limitees au
+ role `Customer`; celles creees depuis une Purchase ou Purchase Line sont
+ limitees au role `Supplier`. Les conditions creees depuis une
+ Charter Party ne peuvent pas utiliser les roles `Supplier` ou `Customer`
+ afin d'eviter la confusion entre conditions armateur/charte et conditions
+ contractuelles achat/vente.
- Les shipments et SOF exposent les conditions Owner, Supplier et Customer
retrouvees depuis le charter party et les lots/lignes physiques.
- Le calcul SOF privilegie la condition appliquee: laytime allowed,
diff --git a/modules/purchase_trade/stock.py b/modules/purchase_trade/stock.py
index 1fb60e0..2f6a1d4 100755
--- a/modules/purchase_trade/stock.py
+++ b/modules/purchase_trade/stock.py
@@ -481,15 +481,7 @@ class CharterCondition(ModelSQL, ModelView):
"Charter Condition"
__name__ = 'charter.condition'
- name = fields.Char("Name")
- charter_party = fields.Many2One(
- 'stock.charter.party', "Charter Party", ondelete='CASCADE')
- purchase = fields.Many2One(
- 'purchase.purchase', "Purchase", ondelete='CASCADE')
- purchase_line = fields.Many2One(
- 'purchase.line', "Purchase Line", ondelete='CASCADE')
- party = fields.Many2One('party.party', "Party")
- party_role = fields.Selection([
+ _party_role_selection = [
(None, ''),
('owner', 'Owner'),
('charterer', 'Charterer'),
@@ -499,7 +491,18 @@ class CharterCondition(ModelSQL, ModelView):
('agent', 'Agent'),
('terminal', 'Terminal'),
('other', 'Other'),
- ], "Party Role")
+ ]
+
+ name = fields.Char("Name")
+ charter_party = fields.Many2One(
+ 'stock.charter.party', "Charter Party", ondelete='CASCADE')
+ purchase = fields.Many2One(
+ 'purchase.purchase', "Purchase", ondelete='CASCADE')
+ purchase_line = fields.Many2One(
+ 'purchase.line', "Purchase Line", ondelete='CASCADE')
+ party = fields.Many2One('party.party', "Party")
+ party_role = fields.Selection(
+ 'get_party_roles', "Party Role")
responsibility = fields.Selection([
(None, ''),
('ours', 'Ours'),
@@ -546,6 +549,45 @@ class CharterCondition(ModelSQL, ModelView):
def default_all_time_saved():
return False
+ @staticmethod
+ def default_party_role():
+ role_context = Transaction().context.get('charter_condition_role')
+ if role_context in {'purchase', 'purchase_line', 'supplier'}:
+ return 'supplier'
+ if role_context in {'sale', 'sale_line', 'customer'}:
+ return 'customer'
+ if role_context == 'charter_party':
+ return 'owner'
+
+ @fields.depends(
+ 'charter_party', 'purchase', 'purchase_line', 'sale', 'sale_line')
+ def get_party_roles(self):
+ role_context = Transaction().context.get('charter_condition_role')
+ if (role_context in {'purchase', 'purchase_line', 'supplier'}
+ or getattr(self, 'purchase', None)
+ or getattr(self, 'purchase_line', None)):
+ return [(None, ''), ('supplier', 'Supplier')]
+ if (role_context in {'sale', 'sale_line', 'customer'}
+ or getattr(self, 'sale', None)
+ or getattr(self, 'sale_line', None)):
+ return [(None, ''), ('customer', 'Customer')]
+ if role_context == 'charter_party' or getattr(
+ self, 'charter_party', None):
+ return [
+ item for item in self._party_role_selection
+ if item[0] not in {'supplier', 'customer'}]
+ return self._party_role_selection
+
+ @classmethod
+ def validate(cls, conditions):
+ super().validate(conditions)
+ for condition in conditions:
+ allowed_roles = {role for role, _ in condition.get_party_roles()}
+ if condition.party_role not in allowed_roles:
+ raise UserError(
+ "This charter condition party role is not allowed "
+ "for its source.")
+
class CharterParty(ModelSQL, ModelView):
"Charter Party"
diff --git a/modules/purchase_trade/tests/test_module.py b/modules/purchase_trade/tests/test_module.py
index 06bf9c5..9f0aac8 100644
--- a/modules/purchase_trade/tests/test_module.py
+++ b/modules/purchase_trade/tests/test_module.py
@@ -9,6 +9,7 @@ from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.exceptions import UserError
+from trytond.transaction import Transaction
from trytond.modules.purchase_trade import valuation as valuation_module
from trytond.modules.purchase_trade import lot as lot_module
from trytond.modules.purchase_trade import purchase as purchase_module
@@ -150,6 +151,44 @@ class PurchaseTradeTestCase(ModuleTestCase):
self.assertEqual(rate.basis, 'per_day')
+ @with_transaction()
+ def test_charter_condition_role_defaults_from_context(self):
+ 'charter condition role defaults to the business source context'
+ Condition = Pool().get('charter.condition')
+
+ with Transaction().set_context(charter_condition_role='sale'):
+ self.assertEqual(Condition.default_party_role(), 'customer')
+ condition = Condition()
+ self.assertEqual(
+ condition.get_party_roles(),
+ [(None, ''), ('customer', 'Customer')])
+
+ with Transaction().set_context(charter_condition_role='purchase'):
+ self.assertEqual(Condition.default_party_role(), 'supplier')
+ condition = Condition()
+ self.assertEqual(
+ condition.get_party_roles(),
+ [(None, ''), ('supplier', 'Supplier')])
+
+ with Transaction().set_context(charter_condition_role='charter_party'):
+ self.assertEqual(Condition.default_party_role(), 'owner')
+ condition = Condition()
+ roles = {role for role, _ in condition.get_party_roles()}
+ self.assertNotIn('supplier', roles)
+ self.assertNotIn('customer', roles)
+
+ @with_transaction()
+ def test_charter_condition_role_rejects_invalid_source_role(self):
+ 'charter condition role is constrained by its source'
+ Condition = Pool().get('charter.condition')
+ condition = Condition()
+ condition.name = 'Invalid supplier condition on sale'
+ condition.party_role = 'supplier'
+
+ with Transaction().set_context(charter_condition_role='sale'):
+ with self.assertRaises(UserError):
+ Condition.validate([condition])
+
@with_transaction()
def test_sof_calculation_reads_applied_condition_rates(self):
'sof demurrage calculation uses the selected charter condition rates'
diff --git a/modules/purchase_trade/view/charter_party_form.xml b/modules/purchase_trade/view/charter_party_form.xml
index 4c5fde6..059c1f1 100644
--- a/modules/purchase_trade/view/charter_party_form.xml
+++ b/modules/purchase_trade/view/charter_party_form.xml
@@ -44,7 +44,7 @@
-
+
diff --git a/modules/purchase_trade/view/purchase_form.xml b/modules/purchase_trade/view/purchase_form.xml
index 36f73f5..f5be4ff 100755
--- a/modules/purchase_trade/view/purchase_form.xml
+++ b/modules/purchase_trade/view/purchase_form.xml
@@ -56,7 +56,7 @@ this repository contains the full copyright notices and license terms. -->
-
+
diff --git a/modules/purchase_trade/view/purchase_line_form.xml b/modules/purchase_trade/view/purchase_line_form.xml
index 57b23f1..6ed1fd0 100755
--- a/modules/purchase_trade/view/purchase_line_form.xml
+++ b/modules/purchase_trade/view/purchase_line_form.xml
@@ -69,7 +69,7 @@ this repository contains the full copyright notices and license terms. -->
-
+
diff --git a/modules/purchase_trade/view/sale_form.xml b/modules/purchase_trade/view/sale_form.xml
index 03cf62a..7c1ba7c 100755
--- a/modules/purchase_trade/view/sale_form.xml
+++ b/modules/purchase_trade/view/sale_form.xml
@@ -56,7 +56,7 @@ this repository contains the full copyright notices and license terms. -->
-
+
diff --git a/modules/purchase_trade/view/sale_line_form.xml b/modules/purchase_trade/view/sale_line_form.xml
index cdad76d..367216f 100755
--- a/modules/purchase_trade/view/sale_line_form.xml
+++ b/modules/purchase_trade/view/sale_line_form.xml
@@ -69,7 +69,7 @@ this repository contains the full copyright notices and license terms. -->
-
+
diff --git a/modules/purchase_trade/view/shipment_in_form.xml b/modules/purchase_trade/view/shipment_in_form.xml
index 1b36f2a..48d7568 100755
--- a/modules/purchase_trade/view/shipment_in_form.xml
+++ b/modules/purchase_trade/view/shipment_in_form.xml
@@ -123,7 +123,7 @@ this repository contains the full copyright notices and license terms. -->
-
+
diff --git a/modules/purchase_trade/view/sof_form.xml b/modules/purchase_trade/view/sof_form.xml
index 14e814a..3b877d6 100755
--- a/modules/purchase_trade/view/sof_form.xml
+++ b/modules/purchase_trade/view/sof_form.xml
@@ -38,7 +38,7 @@
-
+