add country to sla cost
This commit is contained in:
@@ -166,6 +166,16 @@ de negoce physique:
|
|||||||
virtuel, puis uniquement les physiques.
|
virtuel, puis uniquement les physiques.
|
||||||
- detail durable:
|
- detail durable:
|
||||||
`modules/purchase_trade/docs/business-rules.md` BR-PT-020 / BR-PT-021.
|
`modules/purchase_trade/docs/business-rules.md` BR-PT-020 / BR-PT-021.
|
||||||
|
- En execution controller / SLA:
|
||||||
|
- les objectifs de repartition controller utilisent `party.execution.area`
|
||||||
|
(`country.region`).
|
||||||
|
- les couts SLA utilisent `party.execution.place` et peuvent matcher par
|
||||||
|
`country`, par `location`, ou par couple `country + location`.
|
||||||
|
- la creation du fee controller depuis shipment part de
|
||||||
|
`shipment.to_location`; le pays de matching est
|
||||||
|
`shipment.to_location.country`.
|
||||||
|
- priorite cout SLA: couple pays+location, puis location seule, puis pays
|
||||||
|
seul.
|
||||||
|
|
||||||
## 5) Conventions de modification
|
## 5) Conventions de modification
|
||||||
|
|
||||||
|
|||||||
@@ -346,6 +346,34 @@ Owner technique: `a completer`
|
|||||||
- Priorite:
|
- Priorite:
|
||||||
- `importante`
|
- `importante`
|
||||||
|
|
||||||
|
### BR-PT-014-bis - Les couts SLA controller peuvent cibler pays et/ou lieu
|
||||||
|
|
||||||
|
- Intent: permettre de definir le cout d'un controller soit pour un pays, soit
|
||||||
|
pour une location, soit pour un couple pays + location.
|
||||||
|
- Description:
|
||||||
|
- Dans l'onglet `Execution` de `party.party`, les lignes SLA
|
||||||
|
(`party.execution.place`) peuvent porter:
|
||||||
|
- `country`
|
||||||
|
- `location`
|
||||||
|
- ou les deux.
|
||||||
|
- Lors de la creation automatique du fee controller sur un
|
||||||
|
`stock.shipment.in`, le systeme continue de partir de
|
||||||
|
`shipment.to_location`.
|
||||||
|
- Le pays utilise pour le matching est `shipment.to_location.country`.
|
||||||
|
- Priorite de matching:
|
||||||
|
- couple `country + location`
|
||||||
|
- puis `location` seule
|
||||||
|
- puis `country` seul
|
||||||
|
- Resultat attendu:
|
||||||
|
- un cout defini uniquement sur un pays s'applique a toutes les destinations
|
||||||
|
de ce pays.
|
||||||
|
- un cout defini uniquement sur une location s'applique a cette destination,
|
||||||
|
quel que soit le pays porte par la location.
|
||||||
|
- un cout defini sur le couple pays + location est le plus specifique et
|
||||||
|
prime les deux autres.
|
||||||
|
- Priorite:
|
||||||
|
- `importante`
|
||||||
|
|
||||||
### BR-PT-015 - Les weight reports distants par lot partent du weight report global attache au shipment
|
### BR-PT-015 - Les weight reports distants par lot partent du weight report global attache au shipment
|
||||||
|
|
||||||
- Intent: separer la creation du `weight.report` global et l'export detaille
|
- Intent: separer la creation du `weight.report` global et l'export detaille
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ class PartyExecutionPlace(ModelSQL,ModelView):
|
|||||||
__name__ = 'party.execution.place'
|
__name__ = 'party.execution.place'
|
||||||
|
|
||||||
pes = fields.Many2One('party.execution.sla',"Sla")
|
pes = fields.Many2One('party.execution.sla',"Sla")
|
||||||
|
country = fields.Many2One('country.country',"Country")
|
||||||
location = fields.Many2One('stock.location',"Location")
|
location = fields.Many2One('stock.location',"Location")
|
||||||
cost = fields.Numeric("Cost",digits=(16,4))
|
cost = fields.Numeric("Cost",digits=(16,4))
|
||||||
mode = fields.Selection([
|
mode = fields.Selection([
|
||||||
@@ -139,11 +140,38 @@ class Party(metaclass=PoolMeta):
|
|||||||
best_rule = execution
|
best_rule = execution
|
||||||
return best_gap, best_rule
|
return best_gap, best_rule
|
||||||
|
|
||||||
def get_sla_cost(self,location):
|
def get_sla_cost(self, location):
|
||||||
if self.sla:
|
if self.sla:
|
||||||
|
country = getattr(location, 'country', None)
|
||||||
for sla in self.sla:
|
for sla in self.sla:
|
||||||
SlaPlace = Pool().get('party.execution.place')
|
SlaPlace = Pool().get('party.execution.place')
|
||||||
sp = SlaPlace.search([('pes','=', sla.id),('location','=',location)])
|
domain = [
|
||||||
|
('pes', '=', sla.id),
|
||||||
|
[
|
||||||
|
'OR',
|
||||||
|
('location', '=', location),
|
||||||
|
('location', '=', None),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
if country:
|
||||||
|
domain.append([
|
||||||
|
'OR',
|
||||||
|
('country', '=', country),
|
||||||
|
('country', '=', None),
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
domain.append(('country', '=', None))
|
||||||
|
sp = SlaPlace.search(domain)
|
||||||
|
sp = [
|
||||||
|
place for place in sp
|
||||||
|
if place.location or place.country
|
||||||
|
]
|
||||||
|
sp.sort(
|
||||||
|
key=lambda place: (
|
||||||
|
0 if place.location and place.country else
|
||||||
|
1 if place.location else
|
||||||
|
2 if place.country else
|
||||||
|
3))
|
||||||
if sp:
|
if sp:
|
||||||
return sp[0].cost,sp[0].mode,sp[0].currency,sp[0].unit
|
return sp[0].cost,sp[0].mode,sp[0].currency,sp[0].unit
|
||||||
return None, None, None, None
|
return None, None, None, None
|
||||||
|
|||||||
@@ -662,6 +662,79 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
|||||||
party.get_sla_cost(Mock()),
|
party.get_sla_cost(Mock()),
|
||||||
(None, None, None, None))
|
(None, None, None, None))
|
||||||
|
|
||||||
|
def test_get_sla_cost_matches_country_without_location(self):
|
||||||
|
'controller sla helper can match a destination country'
|
||||||
|
Party = Pool().get('party.party')
|
||||||
|
party = Party()
|
||||||
|
sla = Mock(id=1)
|
||||||
|
party.sla = [sla]
|
||||||
|
country = Mock(id=10)
|
||||||
|
location = Mock(country=country)
|
||||||
|
country_place = Mock(
|
||||||
|
country=country,
|
||||||
|
location=None,
|
||||||
|
cost=Decimal('12'),
|
||||||
|
mode='ppack',
|
||||||
|
currency=Mock(id=1),
|
||||||
|
unit=Mock(id=2),
|
||||||
|
)
|
||||||
|
place_model = Mock()
|
||||||
|
place_model.search.return_value = [country_place]
|
||||||
|
|
||||||
|
with patch('trytond.modules.purchase_trade.party.Pool') as PoolMock:
|
||||||
|
PoolMock.return_value.get.return_value = place_model
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
party.get_sla_cost(location),
|
||||||
|
(
|
||||||
|
country_place.cost,
|
||||||
|
country_place.mode,
|
||||||
|
country_place.currency,
|
||||||
|
country_place.unit,
|
||||||
|
))
|
||||||
|
|
||||||
|
def test_get_sla_cost_prioritizes_country_location_pair(self):
|
||||||
|
'controller sla helper prefers country and location over either alone'
|
||||||
|
Party = Pool().get('party.party')
|
||||||
|
party = Party()
|
||||||
|
sla = Mock(id=1)
|
||||||
|
party.sla = [sla]
|
||||||
|
country = Mock(id=10)
|
||||||
|
location = Mock(country=country)
|
||||||
|
country_only = Mock(
|
||||||
|
country=country,
|
||||||
|
location=None,
|
||||||
|
cost=Decimal('12'),
|
||||||
|
mode='ppack',
|
||||||
|
currency=Mock(id=1),
|
||||||
|
unit=Mock(id=2),
|
||||||
|
)
|
||||||
|
location_only = Mock(
|
||||||
|
country=None,
|
||||||
|
location=location,
|
||||||
|
cost=Decimal('14'),
|
||||||
|
mode='perqt',
|
||||||
|
currency=Mock(id=3),
|
||||||
|
unit=Mock(id=4),
|
||||||
|
)
|
||||||
|
pair = Mock(
|
||||||
|
country=country,
|
||||||
|
location=location,
|
||||||
|
cost=Decimal('16'),
|
||||||
|
mode='rate',
|
||||||
|
currency=Mock(id=5),
|
||||||
|
unit=Mock(id=6),
|
||||||
|
)
|
||||||
|
place_model = Mock()
|
||||||
|
place_model.search.return_value = [country_only, location_only, pair]
|
||||||
|
|
||||||
|
with patch('trytond.modules.purchase_trade.party.Pool') as PoolMock:
|
||||||
|
PoolMock.return_value.get.return_value = place_model
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
party.get_sla_cost(location),
|
||||||
|
(pair.cost, pair.mode, pair.currency, pair.unit))
|
||||||
|
|
||||||
def test_get_party_by_name_adds_missing_category_to_existing_party(self):
|
def test_get_party_by_name_adds_missing_category_to_existing_party(self):
|
||||||
'existing parties found by automation gain the requested category when missing'
|
'existing parties found by automation gain the requested category when missing'
|
||||||
Party = Pool().get('party.party')
|
Party = Pool().get('party.party')
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<tree editable="1">
|
<tree editable="1">
|
||||||
|
<field name="country"/>
|
||||||
<field name="location"/>
|
<field name="location"/>
|
||||||
<field name="mode"/>
|
<field name="mode"/>
|
||||||
<field name="cost"/>
|
<field name="cost"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user