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 @@
destination reference.
"""
delivery = brain.getObject().getExplanationValue()
if brain.section_uid != brain.mirror_section_uid:
if delivery.getSourceSectionUid() == brain.section_uid:
return delivery.getSourceReference()
return delivery.getDestinationReference()
is_source = brain.is_source
# If we have a movement which exists for both section uid and mirror section uid,
# we can only guess what reference should be used.
if round(brain.total_quantity - brain.getObject().getQuantity(), 5) == 0:
return delivery.getDestinationReference()
if is_source is None:
# BBB on old data, is_source is NULL
if brain.section_uid != brain.mirror_section_uid:
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):
obj = self.getObject()
if obj is not 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()
if start_date is not None:
timezone = start_date.timezone()
......
......@@ -21,6 +21,7 @@ SELECT
catalog.relative_url as relative_url,
stock.date AS date_utc,
stock.mirror_date AS mirror_date_utc,
stock.is_source,
<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.total_price, <dtml-var precision>)) AS total_price,
......
......@@ -37,6 +37,7 @@ WHERE
getSimulationState[loop_item],
getVariationText[loop_item],
getSubVariationText[loop_item],
0,
])">
</dtml-if>
<dtml-if "getSourceUid[loop_item]">
......@@ -66,6 +67,7 @@ WHERE
getSimulationState[loop_item],
getVariationText[loop_item],
getSubVariationText[loop_item],
1,
])">
</dtml-if>
</dtml-let>
......@@ -100,7 +102,8 @@ INSERT INTO
`portal_type`,
`simulation_state`,
`variation_text`,
`sub_variation_text`
`sub_variation_text`,
`is_source`
)
VALUES
<dtml-in prefix="row" expr="row_list">
......@@ -128,7 +131,8 @@ VALUES
<dtml-sqlvar expr="row_item[20]" 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[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-in>
......
......@@ -27,6 +27,7 @@
getSimulationState[loop_item],
getVariationText[loop_item],
getSubVariationText[loop_item],
0,
])">
</dtml-if>
<dtml-if "getSourceUid[loop_item]">
......@@ -55,6 +56,7 @@
getSimulationState[loop_item],
getVariationText[loop_item],
getSubVariationText[loop_item],
1,
])">
</dtml-if>
</dtml-if>
......@@ -88,7 +90,8 @@ VALUES
<dtml-sqlvar expr="row_item[20]" 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[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-in>
......
......@@ -27,6 +27,7 @@ CREATE TABLE `stock` (
`simulation_state` varchar(255) default '',
`variation_text` VARCHAR(255),
`sub_variation_text` VARCHAR(255),
`is_source` BOOLEAN,
PRIMARY KEY (`uid`, `order_id`),
KEY `quantity` (`quantity`),
KEY `section_uid_portal_type_mirror_section_uid` (`section_uid`, `portal_type`, `mirror_section_uid`),
......
......@@ -2070,6 +2070,14 @@ class TestMovementHistoryList(InventoryAPITestCase):
self.assertEqual(DateTime('2001/02/03 04:05 GMT+3'), brain.date)
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):
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
self._makeMovement(quantity=100,
......@@ -2082,6 +2090,14 @@ class TestMovementHistoryList(InventoryAPITestCase):
self.assertEqual(DateTime('2001/02/03 04:05 GMT+2'), brain.date)
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):
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
self._makeMovement(quantity=100,)
......@@ -2514,6 +2530,15 @@ class TestMovementHistoryList(InventoryAPITestCase):
self.assertEqual(0, mvt_history_list[1].debit_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):
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
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