Commit 82bcd002 authored by Jérome Perrin's avatar Jérome Perrin

core: expose `is_source` on `MovementHistoryListBrain`

This can be useful when making a report on movements and when we list
properties of the movements that depend on the side but are not
directly exposed on MovementHistoryListBrain. One use case was
`Movement_getSpecificReference`, which shows `source_reference` when
the brain is for the source and `destination_reference` otherwise.

With this new approach, instead of guessing we record the "is_source"
information at indexing time, when we know this for sure.

This also simplifies `MovementHistoryListBrain.date` and
`MovementHistoryListBrain.mirror_date` which no longer need to guess
the side and fix a problem that because this guessing was done using
`movement.getSourceUid()` - which cause security errors when users can
not access the source of the movement.
parent 40b47ded
Pipeline #27789 failed with stage
in 0 seconds
...@@ -2,14 +2,15 @@ ...@@ -2,14 +2,15 @@
destination reference. destination reference.
""" """
delivery = brain.getObject().getExplanationValue() delivery = brain.getObject().getExplanationValue()
if brain.section_uid != brain.mirror_section_uid: is_source = brain.is_source
if delivery.getSourceSectionUid() == brain.section_uid:
return delivery.getSourceReference()
return delivery.getDestinationReference()
# If we have a movement which exists for both section uid and mirror section uid, if is_source is None:
# we can only guess what reference should be used. # BBB on old data, is_source is NULL
if round(brain.total_quantity - brain.getObject().getQuantity(), 5) == 0: if brain.section_uid != brain.mirror_section_uid:
return delivery.getDestinationReference() is_source = delivery.getSourceSectionUid() == brain.section_uid
# If we have a movement which exists for both section uid and mirror section uid,
# we can only guess what reference should be used.
is_source = round(brain.total_quantity - brain.getObject().getQuantity(), 5) != 0
return delivery.getSourceReference()
return delivery.getSourceReference() if is_source else delivery.getDestinationReference()
...@@ -362,7 +362,10 @@ class MovementHistoryListBrain(InventoryListBrain): ...@@ -362,7 +362,10 @@ class MovementHistoryListBrain(InventoryListBrain):
obj = self.getObject() obj = self.getObject()
if obj is not None: if obj is not None:
timezone = None timezone = None
if self.node_uid == obj.getSourceUid(): # XXX we could expose an "are we source" property on brain is_source = getattr(self, 'is_source', None)
if is_source is None:
is_source = self.node_uid == obj.getSourceUid()
if is_source:
start_date = obj.getStartDate() start_date = obj.getStartDate()
if start_date is not None: if start_date is not None:
timezone = start_date.timezone() timezone = start_date.timezone()
......
...@@ -21,6 +21,7 @@ SELECT ...@@ -21,6 +21,7 @@ SELECT
catalog.relative_url as relative_url, catalog.relative_url as relative_url,
stock.date AS date_utc, stock.date AS date_utc,
stock.mirror_date AS mirror_date_utc, stock.mirror_date AS mirror_date_utc,
stock.is_source,
<dtml-if expr="precision is not None"> <dtml-if expr="precision is not None">
<dtml-if group_by_expression>SUM</dtml-if>(ROUND(stock.quantity, <dtml-var precision>)) AS total_quantity, <dtml-if group_by_expression>SUM</dtml-if>(ROUND(stock.quantity, <dtml-var precision>)) AS total_quantity,
<dtml-if group_by_expression>SUM</dtml-if>(ROUND(stock.total_price, <dtml-var precision>)) AS total_price, <dtml-if group_by_expression>SUM</dtml-if>(ROUND(stock.total_price, <dtml-var precision>)) AS total_price,
......
...@@ -37,6 +37,7 @@ WHERE ...@@ -37,6 +37,7 @@ WHERE
getSimulationState[loop_item], getSimulationState[loop_item],
getVariationText[loop_item], getVariationText[loop_item],
getSubVariationText[loop_item], getSubVariationText[loop_item],
0,
])"> ])">
</dtml-if> </dtml-if>
<dtml-if "getSourceUid[loop_item]"> <dtml-if "getSourceUid[loop_item]">
...@@ -66,6 +67,7 @@ WHERE ...@@ -66,6 +67,7 @@ WHERE
getSimulationState[loop_item], getSimulationState[loop_item],
getVariationText[loop_item], getVariationText[loop_item],
getSubVariationText[loop_item], getSubVariationText[loop_item],
1,
])"> ])">
</dtml-if> </dtml-if>
</dtml-let> </dtml-let>
...@@ -100,7 +102,8 @@ INSERT INTO ...@@ -100,7 +102,8 @@ INSERT INTO
`portal_type`, `portal_type`,
`simulation_state`, `simulation_state`,
`variation_text`, `variation_text`,
`sub_variation_text` `sub_variation_text`,
`is_source`
) )
VALUES VALUES
<dtml-in prefix="row" expr="row_list"> <dtml-in prefix="row" expr="row_list">
...@@ -128,7 +131,8 @@ VALUES ...@@ -128,7 +131,8 @@ VALUES
<dtml-sqlvar expr="row_item[20]" type="string" optional>, <dtml-sqlvar expr="row_item[20]" type="string" optional>,
<dtml-sqlvar expr="row_item[21]" type="string" optional>, <dtml-sqlvar expr="row_item[21]" type="string" optional>,
<dtml-sqlvar expr="row_item[22]" type="string" optional>, <dtml-sqlvar expr="row_item[22]" type="string" optional>,
<dtml-sqlvar expr="row_item[23]" type="string" optional> <dtml-sqlvar expr="row_item[23]" type="string" optional>,
<dtml-sqlvar expr="row_item[24]" type="int">
) )
<dtml-if sequence-end><dtml-else>,</dtml-if> <dtml-if sequence-end><dtml-else>,</dtml-if>
</dtml-in> </dtml-in>
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
getSimulationState[loop_item], getSimulationState[loop_item],
getVariationText[loop_item], getVariationText[loop_item],
getSubVariationText[loop_item], getSubVariationText[loop_item],
0,
])"> ])">
</dtml-if> </dtml-if>
<dtml-if "getSourceUid[loop_item]"> <dtml-if "getSourceUid[loop_item]">
...@@ -55,6 +56,7 @@ ...@@ -55,6 +56,7 @@
getSimulationState[loop_item], getSimulationState[loop_item],
getVariationText[loop_item], getVariationText[loop_item],
getSubVariationText[loop_item], getSubVariationText[loop_item],
1,
])"> ])">
</dtml-if> </dtml-if>
</dtml-if> </dtml-if>
...@@ -88,7 +90,8 @@ VALUES ...@@ -88,7 +90,8 @@ VALUES
<dtml-sqlvar expr="row_item[20]" type="string" optional>, <dtml-sqlvar expr="row_item[20]" type="string" optional>,
<dtml-sqlvar expr="row_item[21]" type="string" optional>, <dtml-sqlvar expr="row_item[21]" type="string" optional>,
<dtml-sqlvar expr="row_item[22]" type="string" optional>, <dtml-sqlvar expr="row_item[22]" type="string" optional>,
<dtml-sqlvar expr="row_item[23]" type="string" optional> <dtml-sqlvar expr="row_item[23]" type="string" optional>,
<dtml-sqlvar expr="row_item[24]" type="int">
) )
<dtml-if sequence-end><dtml-else>,</dtml-if> <dtml-if sequence-end><dtml-else>,</dtml-if>
</dtml-in> </dtml-in>
......
...@@ -27,6 +27,7 @@ CREATE TABLE `stock` ( ...@@ -27,6 +27,7 @@ CREATE TABLE `stock` (
`simulation_state` varchar(255) default '', `simulation_state` varchar(255) default '',
`variation_text` VARCHAR(255), `variation_text` VARCHAR(255),
`sub_variation_text` VARCHAR(255), `sub_variation_text` VARCHAR(255),
`is_source` BOOLEAN,
PRIMARY KEY (`uid`, `order_id`), PRIMARY KEY (`uid`, `order_id`),
KEY `quantity` (`quantity`), KEY `quantity` (`quantity`),
KEY `section_uid_portal_type_mirror_section_uid` (`section_uid`, `portal_type`, `mirror_section_uid`), KEY `section_uid_portal_type_mirror_section_uid` (`section_uid`, `portal_type`, `mirror_section_uid`),
......
...@@ -2070,6 +2070,14 @@ class TestMovementHistoryList(InventoryAPITestCase): ...@@ -2070,6 +2070,14 @@ class TestMovementHistoryList(InventoryAPITestCase):
self.assertEqual(DateTime('2001/02/03 04:05 GMT+3'), brain.date) self.assertEqual(DateTime('2001/02/03 04:05 GMT+3'), brain.date)
self.assertEqual('GMT+3', brain.date.timezone()) self.assertEqual('GMT+3', brain.date.timezone())
# this also works when `is_source` column is not set, which happens
# with lines that were indexed before `is_source` column was added
self.portal.erp5_sql_connection().query(
'UPDATE `stock` SET `is_source`=NULL WHERE `uid`=%s' % brain.uid)
brain, = getMovementHistoryList(section_uid=self.section.getUid())
self.assertEqual(DateTime('2001/02/03 04:05 GMT+3'), brain.date)
self.assertEqual('GMT+3', brain.date.timezone())
def test_BrainDateTimeZoneStopDate(self): def test_BrainDateTimeZoneStopDate(self):
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
self._makeMovement(quantity=100, self._makeMovement(quantity=100,
...@@ -2082,6 +2090,14 @@ class TestMovementHistoryList(InventoryAPITestCase): ...@@ -2082,6 +2090,14 @@ class TestMovementHistoryList(InventoryAPITestCase):
self.assertEqual(DateTime('2001/02/03 04:05 GMT+2'), brain.date) self.assertEqual(DateTime('2001/02/03 04:05 GMT+2'), brain.date)
self.assertEqual('GMT+2', brain.date.timezone()) self.assertEqual('GMT+2', brain.date.timezone())
# this also works when `is_source` column is not set, which happens
# with lines that were indexed before `is_source` column was added
self.portal.erp5_sql_connection().query(
'UPDATE `stock` SET `is_source`=NULL WHERE `uid`=%s' % brain.uid)
brain, = getMovementHistoryList(mirror_section_uid=self.section.getUid())
self.assertEqual(DateTime('2001/02/03 04:05 GMT+2'), brain.date)
self.assertEqual('GMT+2', brain.date.timezone())
def test_BrainEmptyDate(self): def test_BrainEmptyDate(self):
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
self._makeMovement(quantity=100,) self._makeMovement(quantity=100,)
...@@ -2514,6 +2530,15 @@ class TestMovementHistoryList(InventoryAPITestCase): ...@@ -2514,6 +2530,15 @@ class TestMovementHistoryList(InventoryAPITestCase):
self.assertEqual(0, mvt_history_list[1].debit_price) self.assertEqual(0, mvt_history_list[1].debit_price)
self.assertEqual(-4, mvt_history_list[1].credit_price) self.assertEqual(-4, mvt_history_list[1].credit_price)
def test_is_source(self):
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
self._makeMovement(quantity=100)
brain, = getMovementHistoryList(mirror_node_uid=self.node.getUid())
self.assertTrue(brain.is_source)
brain, = getMovementHistoryList(node_uid=self.node.getUid())
self.assertFalse(brain.is_source)
self.assertIsNotNone(brain.is_source)
def test_group_by_explanation(self): def test_group_by_explanation(self):
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
delivery = self.folder.newContent(portal_type='Dummy Delivery', delivery = self.folder.newContent(portal_type='Dummy Delivery',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment