Commit 449214a5 authored by Jean-Paul Smets's avatar Jean-Paul Smets

Massive update to remove target values in simulation


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@2539 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent bcbcff30
...@@ -45,6 +45,6 @@ class Copy(DeliverySolver): ...@@ -45,6 +45,6 @@ class Copy(DeliverySolver):
""" """
Solve a delivery Solve a delivery
""" """
movement.setQuantity(movement.getTargetQuantity()) movement.setQuantity(movement.getSimulationQuantity())
registerDeliverySolver(Copy) registerDeliverySolver(Copy)
...@@ -40,12 +40,12 @@ class Distribute(DeliverySolver): ...@@ -40,12 +40,12 @@ class Distribute(DeliverySolver):
Solve a delivery by reducing / increasing each simulation movement Solve a delivery by reducing / increasing each simulation movement
it relates to it relates to
""" """
delivery_line_simulation_quantity = float(movement.getSimulationQuantity())
delivery_line_quantity = float(movement.getQuantity()) delivery_line_quantity = float(movement.getQuantity())
delivery_line_target_quantity = float(movement.getTargetQuantity()) if delivery_line_simulation_quantity != delivery_line_quantity:
if delivery_line_quantity != delivery_line_target_quantity: if delivery_line_simulation_quantity != 0 :
if delivery_line_quantity != 0 : # XXXXXXXXXXXXXXXXXXXXXXXXX something special should be done if delivery_line_simulation_quantity == 0 !
# XXXXXXXXXXXXXXXXXXXXXXXXX something special should be done if delivery_line_quantity == 0 ! distribute_ratio = delivery_line_quantity / delivery_line_simulation_quantity
distribute_ratio = delivery_line_target_quantity / delivery_line_quantity
for s in movement.getDeliveryRelatedValueList(): for s in movement.getDeliveryRelatedValueList():
# Reduce quantity # Reduce quantity
s.setQuantity(s.getQuantity() * distribute_ratio) s.setQuantity(s.getQuantity() * distribute_ratio)
...@@ -64,7 +64,6 @@ class Distribute(DeliverySolver): ...@@ -64,7 +64,6 @@ class Distribute(DeliverySolver):
s.setStartDate(movement.getStartDate()) s.setStartDate(movement.getStartDate())
s.setStopDate(movement.getStopDate()) s.setStopDate(movement.getStopDate())
s.diverge() # Make sure everyone knows this simulation movement is inconsistent s.diverge() # Make sure everyone knows this simulation movement is inconsistent
movement.setQuantity(movement.getTargetQuantity())
# No need to touch date since it should be defined at the upper level. # No need to touch date since it should be defined at the upper level.
registerDeliverySolver(Distribute) registerDeliverySolver(Distribute)
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
from Globals import InitializeClass, PersistentMapping from Globals import InitializeClass, PersistentMapping
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Acquisition import aq_base, aq_inner, aq_acquire, aq_chain
from Products.CMFCore.WorkflowCore import WorkflowAction from Products.CMFCore.WorkflowCore import WorkflowAction
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
...@@ -201,10 +202,13 @@ Une ligne tarifaire.""" ...@@ -201,10 +202,13 @@ Une ligne tarifaire."""
security.declarePrivate('_setSource') security.declarePrivate('_setSource')
def _setSource(self, value): def _setSource(self, value):
self._setCategoryMembership('source', value, base=0) self._setCategoryMembership('source', value, base=0)
if self.getPortalType() not in self.getPortalBalanceTransactionLineTypeList(): if self.getPortalType() not in self.getPortalBalanceTransactionLineTypeList() and value not in (None, ''):
source = self.restrictedTraverse(value) source = self.getPortalObject().portal_categories.resolveCategory(value)
destination = self.getDestination() destination = self.getDestination()
if source is not None:
mirror_list = source.getDestinationList() mirror_list = source.getDestinationList()
else:
mirror_list = []
#LOG('_setSource', 0, 'value = %s, mirror_list = %s, destination = %s' % (str(value), str(mirror_list), str(destination))) #LOG('_setSource', 0, 'value = %s, mirror_list = %s, destination = %s' % (str(value), str(mirror_list), str(destination)))
if len(mirror_list) > 0 and destination not in mirror_list: if len(mirror_list) > 0 and destination not in mirror_list:
self._setCategoryMembership('destination', mirror_list[0], base=0) self._setCategoryMembership('destination', mirror_list[0], base=0)
...@@ -219,11 +223,15 @@ Une ligne tarifaire.""" ...@@ -219,11 +223,15 @@ Une ligne tarifaire."""
security.declarePrivate('_setDestination') security.declarePrivate('_setDestination')
def _setDestination(self, value): def _setDestination(self, value):
if self.getPortalType() not in self.getPortalBalanceTransactionLineTypeList(): if self.getPortalType() not in self.getPortalBalanceTransactionLineTypeList() and value not in (None, ''):
self._setCategoryMembership('destination', value, base=0) self._setCategoryMembership('destination', value, base=0)
destination = self.restrictedTraverse(value) destination = self.getPortalObject().portal_categories.resolveCategory(value)
source = self.getSource() source = self.getSource()
if destination is not None:
#LOG('_setSource', 0, 'destination %s' % destination)
mirror_list = destination.getDestinationList() mirror_list = destination.getDestinationList()
else:
mirror_list = []
#LOG('_setDestination', 0, 'value = %s, mirror_list = %s, source = %s' % (str(value), str(mirror_list), str(source))) #LOG('_setDestination', 0, 'value = %s, mirror_list = %s, source = %s' % (str(value), str(mirror_list), str(source)))
if len(mirror_list) > 0 and source not in mirror_list: if len(mirror_list) > 0 and source not in mirror_list:
self._setCategoryMembership('source', mirror_list[0], base=0) self._setCategoryMembership('source', mirror_list[0], base=0)
......
...@@ -206,38 +206,6 @@ class Amount(Base, Variated): ...@@ -206,38 +206,6 @@ class Amount(Base, Variated):
except: except:
LOG("ERP5 WARNING:", 100, 'could not set converted quantity for %s' % self.getRelativeUrl()) LOG("ERP5 WARNING:", 100, 'could not set converted quantity for %s' % self.getRelativeUrl())
security.declareProtected(Permissions.AccessContentsInformation, 'getConvertedTargetQuantity')
def getConvertedTargetQuantity(self):
"""
Converts target_quantity to default unit
"""
#if 1:
try:
#if 1:
resource = self.getResourceValue()
resource_quantity_unit = resource.getDefaultQuantityUnit()
quantity_unit = self.getQuantityUnit()
quantity = self.getTargetQuantity()
converted_quantity = resource.convertQuantity(quantity, quantity_unit, resource_quantity_unit)
#else:
except:
#else:
LOG("ERP5 WARNING:", 100, 'could not convert target_quantity for %s' % self.getRelativeUrl())
converted_quantity = None
return converted_quantity
security.declareProtected(Permissions.ModifyPortalContent, 'setConvertedTargetQuantity')
def setConvertedTargetQuantity(self, value):
try:
#if 1:
resource = self.getResourceValue()
resource_quantity_unit = resource.getDefaultQuantityUnit()
quantity_unit = self.getQuantityUnit()
quantity = resource.convertQuantity(value, resource_quantity_unit, quantity_unit)
self.setTargetQuantity(quantity)
except:
LOG("ERP5 WARNING:", 100, 'could not set converted quantity for %s' % self.getRelativeUrl())
security.declareProtected(Permissions.AccessContentsInformation, 'getNetQuantity') security.declareProtected(Permissions.AccessContentsInformation, 'getNetQuantity')
def getNetQuantity(self): def getNetQuantity(self):
""" """
......
...@@ -164,8 +164,8 @@ Une ligne tarifaire.""" ...@@ -164,8 +164,8 @@ Une ligne tarifaire."""
# Never divergent # Never divergent
return 0 return 0
security.declareProtected(Permissions.AccessContentsInformation, 'getTargetTotalQuantity') security.declareProtected(Permissions.AccessContentsInformation, 'getTotalQuantity')
def getTargetTotalQuantity(self): def getTotalQuantity(self):
""" """
Returns the quantity if no cell or the total quantity if cells Returns the quantity if no cell or the total quantity if cells
""" """
...@@ -174,4 +174,4 @@ Une ligne tarifaire.""" ...@@ -174,4 +174,4 @@ Une ligne tarifaire."""
else: else:
# Use MySQL # Use MySQL
aggregate = self.ContainerLine_zGetTotal()[0] aggregate = self.ContainerLine_zGetTotal()[0]
return aggregate.target_total_quantity return aggregate.total_quantity
This diff is collapsed.
...@@ -240,21 +240,6 @@ Une ligne tarifaire.""" ...@@ -240,21 +240,6 @@ Une ligne tarifaire."""
return self.getTargetQuantity() # We have acquisition here which me should mimic return self.getTargetQuantity() # We have acquisition here which me should mimic
# return None # return None
security.declareProtected( Permissions.AccessContentsInformation, 'getTargetQuantity' )
def getTargetQuantity(self):
"""
Returns the target quantity if defined on the cell
or acquire it
"""
# Call a script on the context
if 'target_quantity' in self.getMappedValuePropertyList([]):
if getattr(aq_base(self), 'target_quantity', None) is not None:
return getattr(self, 'target_quantity')
else:
return self.aq_parent.getProperty('target_quantity')
else:
return None
def _setItemIdList(self, value): def _setItemIdList(self, value):
""" """
Computes total_quantity of all given items and stores this total_quantity Computes total_quantity of all given items and stores this total_quantity
...@@ -353,34 +338,19 @@ Une ligne tarifaire.""" ...@@ -353,34 +338,19 @@ Une ligne tarifaire."""
return self.getParent().getRootDeliveryValue() return self.getParent().getRootDeliveryValue()
# Simulation Consistency Check # Simulation Consistency Check
def getRelatedQuantity(self): def getSimulationQuantity(self):
""" """
Computes the quantities in the simulation Computes the quantities in the simulation
""" """
if isinstance(self, OrderLine): if isinstance(self, OrderLine):
result = self.OrderLine_zGetRelatedQuantity(uid=self.getUid()) result = self.OrderLine_zGetRelatedQuantity(uid=self.getUid())
if len(result) > 0:
return result[0].target_quantity
return None
else:
result = self.DeliveryLine_zGetRelatedQuantity(uid=self.getUid())
if len(result) > 0: if len(result) > 0:
return result[0].quantity return result[0].quantity
return None return None
def getRelatedTargetQuantity(self):
"""
Computes the target quantities in the simulation
"""
if isinstance(self, OrderLine):
result = self.OrderLine_zGetRelatedQuantity(uid=self.getUid())
if len(result) > 0:
return result[0].target_quantity
return None
else: else:
result = self.DeliveryLine_zGetRelatedQuantity(uid=self.getUid()) result = self.DeliveryLine_zGetRelatedQuantity(uid=self.getUid())
if len(result) > 0: if len(result) > 0:
return result[0].target_quantity return result[0].quantity
return None return None
security.declareProtected( Permissions.ModifyPortalContent, 'notifyAfterUpdateRelatedContent' ) security.declareProtected( Permissions.ModifyPortalContent, 'notifyAfterUpdateRelatedContent' )
......
...@@ -209,23 +209,13 @@ Une ligne tarifaire.""" ...@@ -209,23 +209,13 @@ Une ligne tarifaire."""
def _getTotalPrice(self, context): def _getTotalPrice(self, context):
if not self.hasCellContent(): if not self.hasCellContent():
price = self.getPrice(context=context) quantity = self.getQuantity() or 0.0
if price is None: price = 0.0 # Quick and dirty fix XXX
return self.getQuantity() * price
else:
# Use MySQL
aggregate = self.DeliveryLine_zGetTotal()[0]
return aggregate.total_price
def _getTargetTotalPrice(self, context):
if not self.hasCellContent():
target_quantity = self.getTargetQuantity() or 0.0
price = self.getPrice(context=context) or 0.0 price = self.getPrice(context=context) or 0.0
return target_quantity * price return quantity * price
else: else:
# Use MySQL # Use MySQL
aggregate = self.DeliveryLine_zGetTotal()[0] aggregate = self.DeliveryLine_zGetTotal()[0]
return aggregate.target_total_price return aggregate.total_price
security.declareProtected(Permissions.AccessContentsInformation, 'getTotalQuantity') security.declareProtected(Permissions.AccessContentsInformation, 'getTotalQuantity')
def getTotalQuantity(self): def getTotalQuantity(self):
...@@ -239,18 +229,6 @@ Une ligne tarifaire.""" ...@@ -239,18 +229,6 @@ Une ligne tarifaire."""
aggregate = self.DeliveryLine_zGetTotal()[0] aggregate = self.DeliveryLine_zGetTotal()[0]
return aggregate.total_quantity return aggregate.total_quantity
security.declareProtected(Permissions.AccessContentsInformation, 'getTargetTotalQuantity')
def getTargetTotalQuantity(self):
"""
Returns the quantity if no cell or the total quantity if cells
"""
if not self.hasCellContent():
return self.getTargetQuantity()
else:
# Use MySQL
aggregate = self.DeliveryLine_zGetTotal()[0]
return aggregate.target_total_quantity
# Cell Related # Cell Related
security.declareProtected( Permissions.ModifyPortalContent, 'newCellContent' ) security.declareProtected( Permissions.ModifyPortalContent, 'newCellContent' )
def newCellContent(self, id, **kw): def newCellContent(self, id, **kw):
...@@ -320,7 +298,7 @@ Une ligne tarifaire.""" ...@@ -320,7 +298,7 @@ Une ligne tarifaire."""
#LOG('new cell',0,str(k)) #LOG('new cell',0,str(k))
c = self.newCell(*k, **kwd) c = self.newCell(*k, **kwd)
c.edit( domain_base_category_list = self.getVariationBaseCategoryList(), c.edit( domain_base_category_list = self.getVariationBaseCategoryList(),
mapped_value_property_list = ('target_quantity', 'quantity', 'price',), mapped_value_property_list = ('quantity', 'price',),
#predicate_operator = 'SUPERSET_OF', #predicate_operator = 'SUPERSET_OF',
membership_criterion_category = filter(lambda k_item: k_item is not None, k), membership_criterion_category = filter(lambda k_item: k_item is not None, k),
variation_category_list = filter(lambda k_item: k_item is not None, k), variation_category_list = filter(lambda k_item: k_item is not None, k),
...@@ -468,7 +446,7 @@ Une ligne tarifaire.""" ...@@ -468,7 +446,7 @@ Une ligne tarifaire."""
security.declarePrivate('_checkConsistency') security.declarePrivate('_checkConsistency')
def _checkConsistency(self, fixit=0, mapped_value_property_list = ('target_quantity', 'quantity', 'price')): def _checkConsistency(self, fixit=0, mapped_value_property_list = ('quantity', 'price')):
""" """
Check the constitency of transformation elements Check the constitency of transformation elements
""" """
...@@ -507,20 +485,12 @@ Une ligne tarifaire.""" ...@@ -507,20 +485,12 @@ Une ligne tarifaire."""
""" """
Computes the quantities in the simulation Computes the quantities in the simulation
""" """
if not self.hasCellContent():
result = self.DeliveryLine_zGetRelatedQuantity(uid=self.getUid()) result = self.DeliveryLine_zGetRelatedQuantity(uid=self.getUid())
if len(result) > 0: if len(result) > 0:
return result[0].quantity return result[0].quantity
return None return None
def getSimulationTargetQuantity(self):
"""
Computes the target quantities in the simulation
"""
result = self.DeliveryLine_zGetRelatedQuantity(uid=self.getUid())
if len(result) > 0:
return result[0].target_quantity
return None
def getSimulationSourceList(self): def getSimulationSourceList(self):
""" """
Computes the sources in the simulation Computes the sources in the simulation
...@@ -556,26 +526,3 @@ Une ligne tarifaire.""" ...@@ -556,26 +526,3 @@ Une ligne tarifaire."""
""" """
return self.getParent().getRootDeliveryValue() return self.getParent().getRootDeliveryValue()
# Simulation Consistency Check
def getRelatedQuantity(self):
"""
Computes the quantities in the simulation
"""
if not self.hasCellContent():
result = self.DeliveryLine_zGetRelatedQuantity(uid=self.getUid())
if len(result) > 0:
return result[0].quantity
return None
def getRelatedTargetQuantity(self):
"""
Computes the target quantities in the simulation
"""
if not self.hasCellContent():
result = self.DeliveryLine_zGetRelatedQuantity(uid=self.getUid())
if len(result) > 0:
return result[0].target_quantity
return None
...@@ -183,11 +183,8 @@ An ERP5 Rule...""" ...@@ -183,11 +183,8 @@ An ERP5 Rule..."""
delivery_value = c, delivery_value = c,
order_value = c, order_value = c,
quantity = c.getQuantity(), quantity = c.getQuantity(),
target_quantity = c.getTargetQuantity(),
start_date = c.getStartDate(), start_date = c.getStartDate(),
stop_date = c.getStopDate(), stop_date = c.getStopDate(),
target_start_date = c.getTargetStartDate(),
target_stop_date = c.getTargetStopDate(),
deliverable = 1 deliverable = 1
) )
# We must create both order and delivery link in this case # We must create both order and delivery link in this case
...@@ -203,11 +200,8 @@ An ERP5 Rule...""" ...@@ -203,11 +200,8 @@ An ERP5 Rule..."""
delivery_value = delivery_line_object, delivery_value = delivery_line_object,
order_value = delivery_line_object, order_value = delivery_line_object,
quantity = delivery_line_object.getQuantity(), quantity = delivery_line_object.getQuantity(),
target_quantity = delivery_line_object.getTargetQuantity(),
start_date = delivery_line_object.getStartDate(), start_date = delivery_line_object.getStartDate(),
stop_date = delivery_line_object.getStopDate(), stop_date = delivery_line_object.getStopDate(),
target_start_date = delivery_line_object.getTargetStartDate(),
target_stop_date = delivery_line_object.getTargetStopDate(),
deliverable = 1 deliverable = 1
) )
# Source, Destination, Quantity, Date, etc. are # Source, Destination, Quantity, Date, etc. are
......
...@@ -37,7 +37,28 @@ class Domain(PredicateGroup): ...@@ -37,7 +37,28 @@ class Domain(PredicateGroup):
""" """
An abstract class subclassed by reports and mapped values An abstract class subclassed by reports and mapped values
Implements subdomain traversal methods Structure is:
- base domain (like base category)
- sub domain (like category)
Allows to define ranges:
- price between X and Y
- portal_type in (a, b, c)
- price between X and Y and region in (a, b, c)
Reports:
- listbox allows to produce reports
- output to html, pdf or ooffice
- definition through the web (ie. which field in which column, which statistics)
- definition of selection (to list)
- ability for use to "save" favourite report (user reports)
- library of favourite reports (global reports)
- matrixbox allows to produce reports
- output to html, pdf or ooffice
- definition through the web (ie. which base_category or base_domain in which axis)
- definition of selection (to map to matrix)
- ability for use to "save" favourite report (user reports)
- library of favourite reports (global reports)
""" """
meta_type = 'ERP5 Domain' meta_type = 'ERP5 Domain'
portal_type = 'Domain' portal_type = 'Domain'
......
...@@ -106,7 +106,7 @@ class Invoice(AccountingTransaction): ...@@ -106,7 +106,7 @@ class Invoice(AccountingTransaction):
for rule in o.objectValues(): for rule in o.objectValues():
invoice_transaction_rule_list.append(rule) invoice_transaction_rule_list.append(rule)
simulation_line_list += rule.objectValues() simulation_line_list += rule.objectValues()
LOG('buildInvoiceTransactionList simulation_line_list',0,simulation_line_list) #LOG('buildInvoiceTransactionList simulation_line_list',0,simulation_line_list)
from Products.ERP5.MovementGroup import CategoryMovementGroup from Products.ERP5.MovementGroup import CategoryMovementGroup
class_list = [CategoryMovementGroup, ] class_list = [CategoryMovementGroup, ]
root_group = self.portal_simulation.collectMovement(simulation_line_list,class_list=class_list) root_group = self.portal_simulation.collectMovement(simulation_line_list,class_list=class_list)
...@@ -144,9 +144,14 @@ class Invoice(AccountingTransaction): ...@@ -144,9 +144,14 @@ class Invoice(AccountingTransaction):
# add sum of movements to invoice # add sum of movements to invoice
#LOG('buildInvoiceTransactionList group_id',0,group_id) #LOG('buildInvoiceTransactionList group_id',0,group_id)
#LOG('buildInvoiceTransactionList reference_movement',0,str(reference_movement.getRelativeUrl()))
#LOG('buildInvoiceTransactionList reference_movement',0,str(reference_movement.showDict()))
#LOG('buildInvoiceTransactionList reference_movement',0,str(reference_movement.getSource()))
#LOG('buildInvoiceTransactionList reference_movement',0,str(reference_movement.getDestination()))
sale_invoice_transaction_line_item = getattr(self, group_id, None) sale_invoice_transaction_line_item = getattr(self, group_id, None)
if sale_invoice_transaction_line_item is None : if sale_invoice_transaction_line_item is None :
sale_invoice_transaction_line_item = self.newContent(portal_type = self._transaction_line_portal_type sale_invoice_transaction_line_item = self.newContent(
portal_type = self._transaction_line_portal_type
, id = group_id , id = group_id
, source = reference_movement.getSource() , source = reference_movement.getSource()
, destination = reference_movement.getDestination() , destination = reference_movement.getDestination()
...@@ -156,11 +161,13 @@ class Invoice(AccountingTransaction): ...@@ -156,11 +161,13 @@ class Invoice(AccountingTransaction):
sale_invoice_transaction_line_item._setDestinationSection(reference_movement.getDestinationSection()) sale_invoice_transaction_line_item._setDestinationSection(reference_movement.getDestinationSection())
if self.getSourceSection() != reference_movement.getSourceSection(): if self.getSourceSection() != reference_movement.getSourceSection():
sale_invoice_transaction_line_item._setSourceSection(reference_movement.getSourceSection()) sale_invoice_transaction_line_item._setSourceSection(reference_movement.getSourceSection())
#LOG('buildInvoiceTransactionList sale_invoice_transaction_line',0,str(sale_invoice_transaction_line_item.showDict()))
else : else :
sale_invoice_transaction_line_item.edit( sale_invoice_transaction_line_item.edit(
source = reference_movement.getSource() source = reference_movement.getSource()
, destination = reference_movement.getDestination() , destination = reference_movement.getDestination()
, quantity = quantity , quantity = quantity
, force_update = 1
) )
if self.getDestinationSection() != reference_movement.getDestinationSection(): if self.getDestinationSection() != reference_movement.getDestinationSection():
sale_invoice_transaction_line_item._setDestinationSection(reference_movement.getDestinationSection()) sale_invoice_transaction_line_item._setDestinationSection(reference_movement.getDestinationSection())
...@@ -200,7 +207,7 @@ class Invoice(AccountingTransaction): ...@@ -200,7 +207,7 @@ class Invoice(AccountingTransaction):
for rule in o.objectValues(): for rule in o.objectValues():
payment_transaction_rule_list.append(rule) payment_transaction_rule_list.append(rule)
simulation_line_list += rule.objectValues() simulation_line_list += rule.objectValues()
LOG('buildPaymentTransactionList simulation_line_list',0,simulation_line_list) #LOG('buildPaymentTransactionList simulation_line_list',0,simulation_line_list)
# create payment transaction # create payment transaction
accounting_module = self.accounting accounting_module = self.accounting
...@@ -208,17 +215,15 @@ class Invoice(AccountingTransaction): ...@@ -208,17 +215,15 @@ class Invoice(AccountingTransaction):
payment_id = str(accounting_module.generateNewId()) payment_id = str(accounting_module.generateNewId())
payment_transaction = accounting_module.newContent(portal_type = payment_type payment_transaction = accounting_module.newContent(portal_type = payment_type
, id = payment_id , id = payment_id
, source = self.getSource()
, reference = self.getReference() , reference = self.getReference()
, resource = self.getResource() , resource = self.getResource()
, start_date = self.getStartDate() , start_date = self.getStartDate()
, source_payment = self.getSourcePayment() , source_payment = self.getSourcePayment()
, source_section = self.getSourceSection() , source_section = self.getSourceSection()
, destination = self.getDestination()
, destination_payment = self.getDestinationPayment() , destination_payment = self.getDestinationPayment()
, destination_section = self.getDestinationSection() , destination_section = self.getDestinationSection()
) )
LOG('buildPaymentTransactionList payment_transaction', 0, repr(( payment_transaction ))) #LOG('buildPaymentTransactionList payment_transaction', 0, repr(( payment_transaction.showDict() )))
# fill quantity in lines # fill quantity in lines
for movement in simulation_line_list : for movement in simulation_line_list :
...@@ -228,15 +233,24 @@ class Invoice(AccountingTransaction): ...@@ -228,15 +233,24 @@ class Invoice(AccountingTransaction):
payment_transaction_line = getattr(payment_transaction, movement_id, None) payment_transaction_line = getattr(payment_transaction, movement_id, None)
if payment_transaction_line is None : if payment_transaction_line is None :
payment_transaction.newContent(portal_type = 'Accounting Transaction Line' payment_transaction.newContent(
portal_type = 'Accounting Transaction Line'
, id = movement_id , id = movement_id
, quantity = quantity
) )
previous_quantity = 0.0
else : else :
previous_quantity = payment_transaction_line.getQuantity() previous_quantity = payment_transaction_line.getQuantity()
if previous_quantity is not None: if previous_quantity is not None:
quantity = quantity + previous_quantity quantity = quantity + previous_quantity
payment_transaction_line.setQuantity(quantity) payment_transaction_line.edit(
quantity = quantity
, source = movement.getSource()
, destination = movement.getDestination()
, force_update = 1
)
#LOG('buildPaymentTransactionList movement', 0, repr(( movement.showDict() )))
#LOG('buildPaymentTransactionList payment_transaction_line', 0, repr(( payment_transaction_line.showDict() )))
# What do we really need to update in the simulation movement ? # What do we really need to update in the simulation movement ?
if movement.getPortalType() == 'Simulation Movement' : if movement.getPortalType() == 'Simulation Movement' :
......
...@@ -85,9 +85,7 @@ class InvoicingRule(Rule): ...@@ -85,9 +85,7 @@ class InvoicingRule(Rule):
my_context_movement = applied_rule.getParent() my_context_movement = applied_rule.getParent()
LOG('InvoicingRule.expand, my_context_movement.getPhysicalPath()',0,my_context_movement.getPhysicalPath()) LOG('InvoicingRule.expand, my_context_movement.getPhysicalPath()',0,my_context_movement.getPhysicalPath())
LOG('InvoicingRule.expand, my_context_movement.getSource()',0,my_context_movement.getSource()) LOG('InvoicingRule.expand, my_context_movement.getSource()',0,my_context_movement.getSource())
LOG('InvoicingRule.expand, my_context_movement.getTargetSource()',0,my_context_movement.getTargetSource())
LOG('InvoicingRule.expand, my_context_movement.showDict()',0,my_context_movement.showDict()) LOG('InvoicingRule.expand, my_context_movement.showDict()',0,my_context_movement.showDict())
LOG('InvoicingRule.expand, my_context_movement.getSource',0,my_context_movement.getSource())
if my_context_movement.getSource() is not None: if my_context_movement.getSource() is not None:
# We should only expand movements if they have a source # We should only expand movements if they have a source
# otherwise, it creates infinite recursion # otherwise, it creates infinite recursion
...@@ -106,16 +104,16 @@ class InvoicingRule(Rule): ...@@ -106,16 +104,16 @@ class InvoicingRule(Rule):
resource = my_context_movement.getResource() resource = my_context_movement.getResource()
invoice_line._edit( invoice_line._edit(
price = my_context_movement.getPrice(), price = my_context_movement.getPrice(),
target_quantity = my_context_movement.getTargetQuantity(), quantity = my_context_movement.getQuantity(),
target_efficiency = my_context_movement.getTargetEfficiency(), efficiency = my_context_movement.getEfficiency(),
resource = resource, resource = resource,
target_start_date = my_context_movement.getTargetStartDate(), start_date = my_context_movement.getStartDate(),
target_stop_date = my_context_movement.getTargetStartDate(), stop_date = my_context_movement.getStartDate(),
target_source = my_context_movement.getTargetDestination(), source = my_context_movement.getDestination(),
target_source_section = my_context_movement.getTargetSourceSection(), source_section = my_context_movement.getSourceSection(),
quantity_unit = my_context_movement.getQuantityUnit(), quantity_unit = my_context_movement.getQuantityUnit(),
target_destination = my_context_movement.getTargetDestination(), destination = my_context_movement.getDestination(),
target_destination_section = my_context_movement.getTargetDestinationSection(), destination_section = my_context_movement.getDestinationSection(),
deliverable = 1 # We do need to collect invoice lines to build invoices deliverable = 1 # We do need to collect invoice lines to build invoices
) )
# transformation_source.setVariationCategoryList( # transformation_source.setVariationCategoryList(
......
...@@ -269,14 +269,6 @@ a service in a public administration).""" ...@@ -269,14 +269,6 @@ a service in a public administration)."""
else: else:
return None return None
def _getTargetTotalPrice(self, context):
price = self.getPrice(context=context)
quantity = self.getTargetQuantity()
if type(price) in (type(1.0), type(1)) and type(quantity) in (type(1.0), type(1)):
return quantity * price
else:
return None
security.declareProtected(Permissions.AccessContentsInformation, 'getPrice') security.declareProtected(Permissions.AccessContentsInformation, 'getPrice')
def getPrice(self, context=None, REQUEST=None, **kw): def getPrice(self, context=None, REQUEST=None, **kw):
""" """
...@@ -295,12 +287,6 @@ a service in a public administration).""" ...@@ -295,12 +287,6 @@ a service in a public administration)."""
""" """
return self._getTotalPrice(self.asContext(context=context, REQUEST=REQUEST, **kw)) return self._getTotalPrice(self.asContext(context=context, REQUEST=REQUEST, **kw))
security.declareProtected(Permissions.AccessContentsInformation, 'getTargetTotalPrice')
def getTargetTotalPrice(self, context=None, REQUEST=None, **kw):
"""
"""
return self._getTargetTotalPrice(self.asContext(context=context, REQUEST=REQUEST, **kw))
security.declareProtected(Permissions.AccessContentsInformation, 'getTotalQuantity') security.declareProtected(Permissions.AccessContentsInformation, 'getTotalQuantity')
def getTotalQuantity(self): def getTotalQuantity(self):
""" """
...@@ -308,13 +294,6 @@ a service in a public administration).""" ...@@ -308,13 +294,6 @@ a service in a public administration)."""
""" """
return self.getQuantity() return self.getQuantity()
security.declareProtected(Permissions.AccessContentsInformation, 'getTargetTotalQuantity')
def getTargetTotalQuantity(self):
"""
Returns the quantity if no cell or the total quantity if cells
"""
return self.getTargetQuantity()
# Industrial price API # Industrial price API
security.declareProtected(Permissions.AccessContentsInformation, 'getIndustrialPrice') security.declareProtected(Permissions.AccessContentsInformation, 'getIndustrialPrice')
def getIndustrialPrice(self): def getIndustrialPrice(self):
...@@ -418,3 +397,155 @@ a service in a public administration).""" ...@@ -418,3 +397,155 @@ a service in a public administration)."""
security.declareProtected(Permissions.View, 'isSimulated') security.declareProtected(Permissions.View, 'isSimulated')
def isSimulated(self): def isSimulated(self):
return len(self.getDeliveryRelatedValueList()) > 0 or len(self.getOrderRelatedValueList()) > 0 return len(self.getDeliveryRelatedValueList()) > 0 or len(self.getOrderRelatedValueList()) > 0
# New Causality API
security.declareProtected(Permissions.AccessContentsInformation, 'getOrderQuantity')
def getOrderQuantity(self):
"""
Returns the quantity of related order(s)
"""
return self.getQuantity()
security.declareProtected(Permissions.AccessContentsInformation, 'getDeliveryQuantity')
def getDeliveryQuantity(self):
"""
Returns the quantity of related delivery(s)
"""
return self.getQuantity()
security.declareProtected(Permissions.AccessContentsInformation, 'getSimulationQuantity')
def getSimulationQuantity(self):
"""
Returns the sum of quantities in related simulation movements
"""
return self.getQuantity()
security.declareProtected(Permissions.AccessContentsInformation, 'getOrderStartDateList')
def getOrderStartDateList(self):
"""
Returns the start date of related order(s)
"""
return [self.getStartDate()]
security.declareProtected(Permissions.AccessContentsInformation, 'getDeliveryStartDateList')
def getDeliveryStartDateList(self):
"""
Returns the start date of related delivery(s)
"""
return [self.getStartDate()]
security.declareProtected(Permissions.AccessContentsInformation, 'getSimulationStartDateList')
def getSimulationStartDateList(self):
"""
Returns the of start date related simulation movements
"""
return [self.getStartDate()]
security.declareProtected(Permissions.AccessContentsInformation, 'getOrderStopDateList')
def getOrderStopDateList(self):
"""
Returns the stop date of related order(s)
"""
return [self.getStopDate()]
security.declareProtected(Permissions.AccessContentsInformation, 'getDeliveryStopDateList')
def getDeliveryStopDateList(self):
"""
Returns the stop date of related delivery(s)
"""
return [self.getStopDate()]
security.declareProtected(Permissions.AccessContentsInformation, 'getSimulationStopDateList')
def getSimulationStopDateList(self):
"""
Returns the of stop date related simulation movements
"""
return [self.getStopDate()]
security.declareProtected(Permissions.AccessContentsInformation, 'getOrderSourceList')
def getOrderSourceList(self):
"""
Returns the source of related orders
"""
return self.getSourceList()
security.declareProtected(Permissions.AccessContentsInformation, 'getDeliverySourceList')
def getDeliverySourceList(self):
"""
Returns the source of related deliveries
"""
return self.getSourceList()
security.declareProtected(Permissions.AccessContentsInformation, 'getSimulationSourceList')
def getSimulationSourceList(self):
"""
Returns the source of related simulation movements
"""
return self.getSourceList()
security.declareProtected(Permissions.AccessContentsInformation, 'getOrderDestinationList')
def getOrderDestinationList(self):
"""
Returns the destination of related orders
"""
return self.getDestinationList()
security.declareProtected(Permissions.AccessContentsInformation, 'getDeliveryDestinationList')
def getDeliveryDestinationList(self):
"""
Returns the destination of related deliveries
"""
return self.getDestinationList()
security.declareProtected(Permissions.AccessContentsInformation, 'getSimulationDestinationList')
def getSimulationDestinationList(self):
"""
Returns the destination of related simulation movements
"""
return self.getDestinationList()
security.declareProtected(Permissions.AccessContentsInformation, 'getOrderSourceSectionList')
def getOrderSourceSectionList(self):
"""
Returns the source_section of related orders
"""
return self.getSourceSectionList()
security.declareProtected(Permissions.AccessContentsInformation, 'getDeliverySourceSectionList')
def getDeliverySourceSectionList(self):
"""
Returns the source_section of related deliveries
"""
return self.getSourceSectionList()
security.declareProtected(Permissions.AccessContentsInformation, 'getSimulationSourceSectionList')
def getSimulationSourceSectionList(self):
"""
Returns the source_section of related simulation movements
"""
return self.getSourceSectionList()
security.declareProtected(Permissions.AccessContentsInformation, 'getOrderDestinationSectionList')
def getOrderDestinationSectionList(self):
"""
Returns the destination_section of related orders
"""
return self.getDestinationSectionList()
security.declareProtected(Permissions.AccessContentsInformation, 'getDeliveryDestinationSectionList')
def getDeliveryDestinationSectionList(self):
"""
Returns the destination_section of related deliveries
"""
return self.getDestinationSectionList()
security.declareProtected(Permissions.AccessContentsInformation, 'getSimulationDestinationSectionList')
def getSimulationDestinationSectionList(self):
"""
Returns the destination_section of related simulation movements
"""
return self.getDestinationSectionList()
\ No newline at end of file
...@@ -151,7 +151,7 @@ Une ligne tarifaire.""" ...@@ -151,7 +151,7 @@ Une ligne tarifaire."""
c = self.newCell(*k, **kwd) c = self.newCell(*k, **kwd)
#LOG('OrderLine _setVariationCategoryList', 0, 'k = %s, c = %s, self.getVariationBaseCategoryList() = %s' % (repr(k), repr(c), repr(self.getVariationBaseCategoryList()))) #LOG('OrderLine _setVariationCategoryList', 0, 'k = %s, c = %s, self.getVariationBaseCategoryList() = %s' % (repr(k), repr(c), repr(self.getVariationBaseCategoryList())))
c.edit( domain_base_category_list = self.getVariationBaseCategoryList(), c.edit( domain_base_category_list = self.getVariationBaseCategoryList(),
mapped_value_property_list = ('target_quantity', 'price',), mapped_value_property_list = ('quantity', 'price',),
predicate_operator = 'SUPERSET_OF', predicate_operator = 'SUPERSET_OF',
predicate_value = filter(lambda k_item: k_item is not None, k), predicate_value = filter(lambda k_item: k_item is not None, k),
variation_category_list = filter(lambda k_item: k_item is not None, k), variation_category_list = filter(lambda k_item: k_item is not None, k),
...@@ -168,7 +168,7 @@ Une ligne tarifaire.""" ...@@ -168,7 +168,7 @@ Une ligne tarifaire."""
self._delObject(k) self._delObject(k)
security.declarePrivate('_checkConsistency') security.declarePrivate('_checkConsistency')
def _checkConsistency(self, fixit=0, mapped_value_property_list = ('target_quantity', 'price')): def _checkConsistency(self, fixit=0, mapped_value_property_list = ('quantity', 'price')):
""" """
Check the constitency of transformation elements Check the constitency of transformation elements
""" """
...@@ -211,20 +211,11 @@ Une ligne tarifaire.""" ...@@ -211,20 +211,11 @@ Une ligne tarifaire."""
self.aq_parent.activate()._createOrderRule() self.aq_parent.activate()._createOrderRule()
# Simulation Consistency Check # Simulation Consistency Check
def getRelatedQuantity(self): def getSimulationQuantity(self):
"""
Computes the quantities in the simulation
"""
result = self.OrderLine_zGetRelatedQuantity(uid=self.getUid())
if len(result) > 0:
return result[0].quantity
return None
def getRelatedTargetQuantity(self):
""" """
Computes the target quantities in the simulation Computes the target quantities in the simulation
""" """
result = self.OrderLine_zGetRelatedQuantity(uid=self.getUid()) result = self.OrderLine_zGetRelatedQuantity(uid=self.getUid())
if len(result) > 0: if len(result) > 0:
return result[0].target_quantity return result[0].quantity
return None return None
...@@ -146,7 +146,7 @@ An ERP5 Rule...""" ...@@ -146,7 +146,7 @@ An ERP5 Rule..."""
# eventually delete movement which do not exist anylonger # eventually delete movement which do not exist anylonger
existing_uid_list = [] existing_uid_list = []
for movement in applied_rule.contentValues(filter={'portal_type':applied_rule.getPortalMovementTypeList()}): for movement in applied_rule.contentValues(filter={'portal_type':applied_rule.getPortalMovementTypeList()}):
#LOG('Movement', 0, str(movement)) LOG('Movement', 0, str(movement))
order_value = movement.getOrderValue(portal_type=applied_rule.getPortalOrderMovementTypeList()) order_value = movement.getOrderValue(portal_type=applied_rule.getPortalOrderMovementTypeList())
if order_value is None: if order_value is None:
movement.flushActivity(invoke=0) movement.flushActivity(invoke=0)
...@@ -156,7 +156,7 @@ An ERP5 Rule...""" ...@@ -156,7 +156,7 @@ An ERP5 Rule..."""
existing_uid_list += [order_value.getUid()] existing_uid_list += [order_value.getUid()]
elif order_value.hasCellContent(): elif order_value.hasCellContent():
# Do not keep head of cells # Do not keep head of cells
#LOG('INFO', 0, 'Order Rule Deleting Simulatino Movement %s' % movement.getRelativeUrl()) LOG('INFO', 0, 'Order Rule Deleting Simulatino Movement %s' % movement.getRelativeUrl())
order_value.flushActivity(invoke=0) order_value.flushActivity(invoke=0)
applied_rule._delObject(movement.getId()) # XXXX Make sur this is not deleted if already in delivery applied_rule._delObject(movement.getId()) # XXXX Make sur this is not deleted if already in delivery
else: else:
...@@ -164,16 +164,22 @@ An ERP5 Rule...""" ...@@ -164,16 +164,22 @@ An ERP5 Rule..."""
# Copy each movement (line or cell) from the order # Copy each movement (line or cell) from the order
for order_line_object in my_order.contentValues(filter={'portal_type':applied_rule.getPortalMovementTypeList()}): for order_line_object in my_order.contentValues(filter={'portal_type':applied_rule.getPortalMovementTypeList()}):
LOG('OrderRule.expand, examining:',0,order_line_object.getPhysicalPath())
try: try:
if order_line_object.hasCellContent(): if order_line_object.hasCellContent():
for c in order_line_object.getCellValueList(): for c in order_line_object.getCellValueList():
#LOG('Cell in', 0, '%s %s' % (c.getUid(), existing_uid_list)) LOG('Cell in', 0, '%s %s' % (c.getUid(), existing_uid_list))
if c.getUid() not in existing_uid_list: if c.getUid() not in existing_uid_list:
new_id = order_line_object.getId() + '_' + c.getId() new_id = order_line_object.getId() + '_' + c.getId()
#LOG('Create Cell', 0, str(new_id)) LOG('Create Cell', 0, str(new_id))
new_line = applied_rule.newContent(type_name=delivery_line_type, new_line = applied_rule.newContent(type_name=delivery_line_type,
id=new_id, id=new_id,
order_value = c, order_value = c,
quantity = c.getQuantity(),
source = c.getSource(),
destination = c.getDestination(),
source_section = c.getSourceSection(),
destination_section = c.getDestinationSection(),
deliverable = 1 deliverable = 1
) )
LOG('OrderRule.expand, object created:',0,new_line.getPhysicalPath()) LOG('OrderRule.expand, object created:',0,new_line.getPhysicalPath())
...@@ -182,16 +188,21 @@ An ERP5 Rule...""" ...@@ -182,16 +188,21 @@ An ERP5 Rule..."""
else: else:
if order_line_object.getUid() not in existing_uid_list: if order_line_object.getUid() not in existing_uid_list:
new_id = order_line_object.getId() new_id = order_line_object.getId()
#LOG('Line', 0, str(new_id)) LOG('Line', 0, str(new_id))
new_line = applied_rule.newContent(type_name=delivery_line_type, new_line = applied_rule.newContent(type_name=delivery_line_type,
container=applied_rule, container=applied_rule,
id=new_id, id=new_id,
order_value = order_line_object, order_value = order_line_object,
quantity = order_line_object.getQuantity(),
source = order_line_object.getSource(),
destination = order_line_object.getDestination(),
source_section = order_line_object.getSourceSection(),
destination_section = order_line_object.getDestinationSection(),
deliverable = 1 deliverable = 1
) )
LOG('OrderRule.expand, object created:',0,new_line.getPhysicalPath()) LOG('OrderRule.expand, object created:',0,new_line.getPhysicalPath())
new_line.immediateReindexObject() new_line.immediateReindexObject()
#LOG('After Create Cell', 0, str(new_id)) LOG('After Create Cell', 0, str(new_id))
# Source, Destination, Quantity, Date, etc. are # Source, Destination, Quantity, Date, etc. are
# acquired from the order and need not to be copied. # acquired from the order and need not to be copied.
except AttributeError: except AttributeError:
......
...@@ -69,7 +69,7 @@ class PaymentRule(Rule): ...@@ -69,7 +69,7 @@ class PaymentRule(Rule):
if 'receivable' in movement.getId() : if 'receivable' in movement.getId() :
parent = movement.getParent() parent = movement.getParent()
if parent.getPortalType()=='Applied Rule' and parent.getSpecialiseId()=='default_invoice_transaction_rule': if parent.getPortalType()=='Applied Rule' and parent.getSpecialiseId()=='default_invoice_transaction_rule':
LOG('PaymentRule.test :', 0, repr(( 'applies with', movement, parent ))) #LOG('PaymentRule.test :', 0, repr(( 'applies with', movement, parent )))
return 1 return 1
return 0 return 0
...@@ -103,8 +103,20 @@ class PaymentRule(Rule): ...@@ -103,8 +103,20 @@ class PaymentRule(Rule):
type_name = payment_line_type, type_name = payment_line_type,
id = receivable_id) id = receivable_id)
bank_movement.setQuantity(my_parent_movement.getQuantity()) bank_movement.edit(
receivable_movement.setQuantity(0 - my_parent_movement.getQuantity()) quantity = my_parent_movement.getQuantity(),
source = 'account/banques_etablissements_financiers', # XXX Not Generic
destination = 'account/banques_etablissements_financiers', # XXX Not Generic
source_section = my_parent_movement.getSourceSection(),
destination_section = my_parent_movement.getDestinationSection(),
)
receivable_movement.edit(
quantity = - my_parent_movement.getQuantity(),
source = 'account/creance_client', # XXX Not Generic
destination = 'account/dette_fournisseur', # XXX Not Generic
source_section = my_parent_movement.getSourceSection(),
destination_section = my_parent_movement.getDestinationSection(),
)
Rule.expand(self, applied_rule, **kw) Rule.expand(self, applied_rule, **kw)
......
...@@ -226,6 +226,7 @@ identify a bank account.""" ...@@ -226,6 +226,7 @@ identify a bank account."""
membership_criterion_category_list = [] membership_criterion_category_list = []
membership_criterion_base_category_list = [] membership_criterion_base_category_list = []
multimembership_criterion_base_category_list = [] multimembership_criterion_base_category_list = []
test_method_id_list = []
criterion_property_list = [] criterion_property_list = []
for c in category_list: for c in category_list:
bc = c.split('/')[0] bc = c.split('/')[0]
...@@ -243,12 +244,14 @@ identify a bank account.""" ...@@ -243,12 +244,14 @@ identify a bank account."""
predicate_value.getMembershipCriterionBaseCategoryList()) predicate_value.getMembershipCriterionBaseCategoryList())
multimembership_criterion_base_category_list.extend( multimembership_criterion_base_category_list.extend(
predicate_value.getMultimembershipCriterionBaseCategoryList()) predicate_value.getMultimembershipCriterionBaseCategoryList())
test_method_id_list += list(predicate_value.getTestMethodIdList() or [])
for p in predicate_value.getCriterionList(): for p in predicate_value.getCriterionList():
self.setCriterion(p.property, identity=p.identity, min=p.min, max=p.max) self.setCriterion(p.property, identity=p.identity, min=p.min, max=p.max)
self.setCriterionPropertyList(criterion_property_list) self.setCriterionPropertyList(criterion_property_list)
self.setMembershipCriterionCategoryList(membership_criterion_category_list) self._setMembershipCriterionCategoryList(membership_criterion_category_list)
self.setMembershipCriterionBaseCategoryList(membership_criterion_base_category_list) self._setMembershipCriterionBaseCategoryList(membership_criterion_base_category_list)
self.setMultimembershipCriterionBaseCategoryList(multimembership_criterion_base_category_list) self._setMultimembershipCriterionBaseCategoryList(multimembership_criterion_base_category_list)
self._setTestMethodIdList(test_method_id_list)
self.reindexObject() self.reindexObject()
# Predicate handling # Predicate handling
......
...@@ -435,3 +435,18 @@ a service in a public administration).""" ...@@ -435,3 +435,18 @@ a service in a public administration)."""
return 0 return 0
getDeliverable = isDeliverable getDeliverable = isDeliverable
# Simulation Dates - acquire target dates
security.declareProtected(Permissions.AccessContentsInformation, 'getOrderStartDate')
def getOrderStartDate(self):
order_value = self.getOrderValue()
if order_value is not None:
return order_value.getStartDate()
security.declareProtected(Permissions.AccessContentsInformation, 'getOrderStopDate')
def getOrderStopDate(self):
order_value = self.getOrderValue()
if order_value is not None:
return order_value.getStopDate()
\ No newline at end of file
...@@ -232,15 +232,15 @@ An ERP5 Rule...""" ...@@ -232,15 +232,15 @@ An ERP5 Rule..."""
produced_resource = applied_rule[new_id] produced_resource = applied_rule[new_id]
produced_resource._edit( produced_resource._edit(
target_start_date = my_context_movement.getTargetStartDate(), start_date = my_context_movement.getStartDate(),
target_stop_date = my_context_movement.getTargetStartDate(), stop_date = my_context_movement.getStartDate(),
resource = my_context_movement.getResource(), resource = my_context_movement.getResource(),
target_quantity = my_context_movement.getTargetQuantity() + lost_quantity, quantity = my_context_movement.getQuantity() + lost_quantity,
target_source_list = (), source_list = (),
target_source_section_list = (), source_section_list = (),
quantity_unit = my_context_movement.getQuantityUnit(), quantity_unit = my_context_movement.getQuantityUnit(),
target_destination_section = production_section, destination_section = production_section,
target_destination = production_node, destination = production_node,
deliverable = 1 deliverable = 1
) )
# Mising quantity unit conversion for my_quantity !!!! XXXX # Mising quantity unit conversion for my_quantity !!!! XXXX
...@@ -270,15 +270,15 @@ An ERP5 Rule...""" ...@@ -270,15 +270,15 @@ An ERP5 Rule..."""
if amount_line['quantity'] != 0.0: if amount_line['quantity'] != 0.0:
# Only create line if it is not 0.0 # Only create line if it is not 0.0
transformed_resource._edit( transformed_resource._edit(
target_start_date = my_context_movement.getTargetStartDate(), start_date = my_context_movement.getStartDate(),
target_stop_date = my_context_movement.getTargetStartDate(), stop_date = my_context_movement.getStartDate(),
target_quantity = amount_line['quantity'] * my_quantity, quantity = amount_line['quantity'] * my_quantity,
target_efficiency = amount_line['efficiency'], efficiency = amount_line['efficiency'],
resource_value = amount_line['resource'], resource_value = amount_line['resource'],
quantity_unit = amount_line['quantity_unit'], quantity_unit = amount_line['quantity_unit'],
target_source = production_node, source = production_node,
target_source_section = production_section, source_section = production_section,
target_destination_list = (), destination_list = (),
deliverable = 1 deliverable = 1
) )
LOG('TransformationRule.expand transformed_resource.getPhysicalPath()',0,transformed_resource.getPhysicalPath()) LOG('TransformationRule.expand transformed_resource.getPhysicalPath()',0,transformed_resource.getPhysicalPath())
......
...@@ -140,7 +140,7 @@ portal_balance_transaction_line_type_list = ('Balance Transaction Line',) ...@@ -140,7 +140,7 @@ portal_balance_transaction_line_type_list = ('Balance Transaction Line',)
## Inventory States ## Inventory States
portal_current_inventory_state_list = ('delivered', 'started', 'stopped', 'invoiced') # invoiced is Coramy specific and should be removed portal_current_inventory_state_list = ('delivered', 'started', 'stopped', 'invoiced') # invoiced is Coramy specific and should be removed
portal_target_inventory_state_list = ('ready', 'delivered', 'started', 'stopped', 'invoiced') # if simulation_state in target_list, target_quantity should be considered instead of quantity for stock indexing portal_target_inventory_state_list = ('ready', 'delivered', 'started', 'stopped', 'invoiced') # if simulation_state in target_list, target_quantity should be considered instead of quantity for stock indexing XXX why do we need two inventory_state_list ?
portal_draft_order_state_list = ('cancelled', 'draft', 'auto_planned' ) portal_draft_order_state_list = ('cancelled', 'draft', 'auto_planned' )
portal_planned_order_state_list = ('planned', 'ordered', ) portal_planned_order_state_list = ('planned', 'ordered', )
portal_reserved_inventory_state_list = ('confirmed', 'getting_ready', 'ready') portal_reserved_inventory_state_list = ('confirmed', 'getting_ready', 'ready')
......
...@@ -9,9 +9,7 @@ def setBaseAcquisition(self): ...@@ -9,9 +9,7 @@ def setBaseAcquisition(self):
# we should not use causality here because of production reports # we should not use causality here because of production reports
# for which source or destination can be None (ie. different from Production Order) # for which source or destination can be None (ie. different from Production Order)
for bc in ('source', 'destination', for bc in ('source', 'destination',
'target_source', 'target_destination', 'source_section', 'destination_section',):
'source_section', 'destination_section',
'target_source_section', 'target_destination_section',):
if not hasattr(pc, bc): if not hasattr(pc, bc):
addBaseCategory(pc, bc) addBaseCategory(pc, bc)
pc[bc].setAcquisitionBaseCategoryList(('delivery', 'order', 'parent', )) pc[bc].setAcquisitionBaseCategoryList(('delivery', 'order', 'parent', ))
......
...@@ -48,11 +48,11 @@ class RootMovementGroup: ...@@ -48,11 +48,11 @@ class RootMovementGroup:
This sets an appropriate nested class. This sets an appropriate nested class.
""" """
LOG('RootGroup.setNestedClass, class_list:',0,class_list) #LOG('RootGroup.setNestedClass, class_list:',0,class_list)
for i in range(len(class_list)): for i in range(len(class_list)):
LOG('RootGroup.setNestedClass, class_list[i]:',0,class_list[i]) #LOG('RootGroup.setNestedClass, class_list[i]:',0,class_list[i])
#LOG('RootGroup.setNestedClass, class_list[i].getId():',0,class_list[i].getId()) #LOG('RootGroup.setNestedClass, class_list[i].getId():',0,class_list[i].getId())
LOG('RootGroup.setNestedClass, self.__class__:',0,self.__class__) #LOG('RootGroup.setNestedClass, self.__class__:',0,self.__class__)
if class_list[i] == self.__class__: if class_list[i] == self.__class__:
break break
else: else:
...@@ -71,7 +71,7 @@ class RootMovementGroup: ...@@ -71,7 +71,7 @@ class RootMovementGroup:
def appendGroup(self, movement,class_list=None): def appendGroup(self, movement,class_list=None):
if self.nested_class is not None: if self.nested_class is not None:
LOG('RootGroup.appendGroup, class_list',0,class_list) #LOG('RootGroup.appendGroup, class_list',0,class_list)
nested_instance = self.nested_class(movement=movement,class_list=class_list) nested_instance = self.nested_class(movement=movement,class_list=class_list)
self.group_list.append(nested_instance) self.group_list.append(nested_instance)
...@@ -84,7 +84,7 @@ class RootMovementGroup: ...@@ -84,7 +84,7 @@ class RootMovementGroup:
movement_in_group = 1 movement_in_group = 1
break break
if movement_in_group == 0 : if movement_in_group == 0 :
LOG('RootGroup.append, class_list',0,class_list) #LOG('RootGroup.append, class_list',0,class_list)
self.appendGroup(movement,class_list=class_list) self.appendGroup(movement,class_list=class_list)
allow_class(RootMovementGroup) allow_class(RootMovementGroup)
...@@ -93,7 +93,7 @@ class OrderMovementGroup(RootMovementGroup): ...@@ -93,7 +93,7 @@ class OrderMovementGroup(RootMovementGroup):
def __init__(self,movement,**kw): def __init__(self,movement,**kw):
LOG('OrderMovementGroup.__init__, kw:',0,kw) #LOG('OrderMovementGroup.__init__, kw:',0,kw)
RootMovementGroup.__init__(self,movement,**kw) RootMovementGroup.__init__(self,movement,**kw)
if hasattr(movement, 'getRootAppliedRule'): if hasattr(movement, 'getRootAppliedRule'):
# This is a simulation movement # This is a simulation movement
...@@ -146,32 +146,20 @@ class PathMovementGroup(RootMovementGroup): ...@@ -146,32 +146,20 @@ class PathMovementGroup(RootMovementGroup):
def __init__(self,movement,**kw): def __init__(self,movement,**kw):
RootMovementGroup.__init__(self,movement,**kw) RootMovementGroup.__init__(self,movement,**kw)
self.source = movement.getSource() self.source = movement.getSource()
LOG('PathGroup.__init__ source',0,self.source) #LOG('PathGroup.__init__ source',0,self.source)
self.destination = movement.getDestination() self.destination = movement.getDestination()
LOG('PathGroup.__init__ destination',0,self.destination) #LOG('PathGroup.__init__ destination',0,self.destination)
self.source_section = movement.getSourceSection() self.source_section = movement.getSourceSection()
LOG('PathGroup.__init__ source_section',0,self.source_section) #LOG('PathGroup.__init__ source_section',0,self.source_section)
self.destination_section = movement.getDestinationSection() self.destination_section = movement.getDestinationSection()
LOG('PathGroup.__init__ destination_section',0,self.destination_section) #LOG('PathGroup.__init__ destination_section',0,self.destination_section)
self.target_source = movement.getTargetSource()
LOG('PathGroup.__init__ target_source',0,self.target_source)
self.target_destination = movement.getTargetDestination()
LOG('PathGroup.__init__ target_destination',0,self.target_destination)
self.target_source_section = movement.getTargetSourceSection()
LOG('PathGroup.__init__ target_source_section',0,self.target_source_section)
self.target_destination_section = movement.getTargetDestinationSection()
LOG('PathGroup.__init__ target_destination_section',0,self.target_destination_section)
def test(self,movement): def test(self,movement):
if movement.getSource() == self.source and \ if movement.getSource() == self.source and \
movement.getDestination() == self.destination and \ movement.getDestination() == self.destination and \
movement.getSourceSection() == self.source_section and \ movement.getSourceSection() == self.source_section and \
movement.getDestinationSection() == self.destination_section and \ movement.getDestinationSection() == self.destination_section :
movement.getTargetSource() == self.target_source and \
movement.getTargetDestination() == self.target_destination and \
movement.getTargetSourceSection() == self.target_source_section and \
movement.getTargetDestinationSection() == self.target_destination_section :
return 1 return 1
else : else :
...@@ -183,8 +171,6 @@ class DateMovementGroup(RootMovementGroup): ...@@ -183,8 +171,6 @@ class DateMovementGroup(RootMovementGroup):
def __init__(self,movement,**kw): def __init__(self,movement,**kw):
RootMovementGroup.__init__(self,movement,**kw) RootMovementGroup.__init__(self,movement,**kw)
self.target_start_date = movement.getTargetStartDate()
self.target_stop_date = movement.getTargetStopDate()
self.start_date = movement.getStartDate() self.start_date = movement.getStartDate()
self.stop_date = movement.getStopDate() self.stop_date = movement.getStopDate()
...@@ -236,7 +222,7 @@ class BaseVariantMovementGroup(RootMovementGroup): ...@@ -236,7 +222,7 @@ class BaseVariantMovementGroup(RootMovementGroup):
RootMovementGroup.__init__(self,movement,**kw) RootMovementGroup.__init__(self,movement,**kw)
self.base_category_list = movement.getVariationBaseCategoryList() self.base_category_list = movement.getVariationBaseCategoryList()
if self.base_category_list is None: if self.base_category_list is None:
LOG('BaseVariantGroup __init__', 0, 'movement = %s, movement.showDict() = %s' % (repr(movement), repr(movement.showDict()))) #LOG('BaseVariantGroup __init__', 0, 'movement = %s, movement.showDict() = %s' % (repr(movement), repr(movement.showDict())))
self.base_category_list = [] self.base_category_list = []
def test(self,movement): def test(self,movement):
...@@ -245,7 +231,7 @@ class BaseVariantMovementGroup(RootMovementGroup): ...@@ -245,7 +231,7 @@ class BaseVariantMovementGroup(RootMovementGroup):
#LOG('BaseVariantGroup', 0, 'self.base_category_list = %s, movement = %s, movement.getVariationBaseCategoryList() = %s' % (repr(self.base_category_list), repr(movement), repr(movement.getVariationBaseCategoryList()))) #LOG('BaseVariantGroup', 0, 'self.base_category_list = %s, movement = %s, movement.getVariationBaseCategoryList() = %s' % (repr(self.base_category_list), repr(movement), repr(movement.getVariationBaseCategoryList())))
movement_base_category_list = movement.getVariationBaseCategoryList() movement_base_category_list = movement.getVariationBaseCategoryList()
if movement_base_category_list is None: if movement_base_category_list is None:
LOG('BaseVariantGroup test', 0, 'movement = %s, movement.showDict() = %s' % (repr(movement), repr(movement.showDict()))) #LOG('BaseVariantGroup test', 0, 'movement = %s, movement.showDict() = %s' % (repr(movement), repr(movement.showDict())))
movement_base_category_list = [] movement_base_category_list = []
if len(self.base_category_list) == len(movement_base_category_list): if len(self.base_category_list) == len(movement_base_category_list):
for category in movement_base_category_list: for category in movement_base_category_list:
...@@ -263,7 +249,7 @@ class VariantMovementGroup(RootMovementGroup): ...@@ -263,7 +249,7 @@ class VariantMovementGroup(RootMovementGroup):
RootMovementGroup.__init__(self,movement,**kw) RootMovementGroup.__init__(self,movement,**kw)
self.category_list = movement.getVariationCategoryList() self.category_list = movement.getVariationCategoryList()
if self.category_list is None: if self.category_list is None:
LOG('VariantGroup __init__', 0, 'movement = %s, movement.showDict() = %s' % (repr(movement), repr(movement.showDict()))) #LOG('VariantGroup __init__', 0, 'movement = %s, movement.showDict() = %s' % (repr(movement), repr(movement.showDict())))
self.category_list = [] self.category_list = []
def test(self,movement): def test(self,movement):
...@@ -271,7 +257,7 @@ class VariantMovementGroup(RootMovementGroup): ...@@ -271,7 +257,7 @@ class VariantMovementGroup(RootMovementGroup):
categories_identity = 0 categories_identity = 0
movement_category_list = movement.getVariationCategoryList() movement_category_list = movement.getVariationCategoryList()
if movement_category_list is None: if movement_category_list is None:
LOG('VariantGroup test', 0, 'movement = %s, movement.showDict() = %s' % (repr(movement), repr(movement.showDict()))) #LOG('VariantGroup test', 0, 'movement = %s, movement.showDict() = %s' % (repr(movement), repr(movement.showDict())))
movement_category_list = [] movement_category_list = []
if len(self.category_list) == len(movement_category_list): if len(self.category_list) == len(movement_category_list):
for category in movement_category_list: for category in movement_category_list:
......
...@@ -90,7 +90,6 @@ class Amount: ...@@ -90,7 +90,6 @@ class Amount:
'acquisition_mask_value' : 1, 'acquisition_mask_value' : 1,
'acquisition_accessor_id' : 'getQuantity', 'acquisition_accessor_id' : 'getQuantity',
'acquisition_depends' : None, 'acquisition_depends' : None,
'alt_accessor_id' : ('getTargetQuantity', ),
'mode' : 'w' }, 'mode' : 'w' },
{ 'id' : 'efficiency', { 'id' : 'efficiency',
'description' : """The efficiency.""", 'description' : """The efficiency.""",
...@@ -102,34 +101,10 @@ class Amount: ...@@ -102,34 +101,10 @@ class Amount:
'acquisition_mask_value' : 1, 'acquisition_mask_value' : 1,
'acquisition_accessor_id' : 'getEfficiency', 'acquisition_accessor_id' : 'getEfficiency',
'acquisition_depends' : None, 'acquisition_depends' : None,
'alt_accessor_id' : ('getTargetEfficiency', ),
#'get_adapter_id' : #'get_adapter_id' :
#'set_adapater_id' : #'set_adapater_id' :
#'has_adapater_id' : #'has_adapater_id' :
'mode' : 'w' }, 'mode' : 'w' },
# Planning
{ 'id' : 'target_quantity',
'description' : """The target quantity of resource.""",
'type' : 'float',
'default' : 0.0,
'acquisition_base_category' : ('order',),
'acquisition_portal_type' : Expression('python: portal.getPortalAcquisitionMovementTypeList() + portal.getPortalOrderTypeList()'),
'acquisition_copy_value' : 0,
'acquisition_mask_value' : 1,
'acquisition_accessor_id' : 'getTargetQuantity',
'acquisition_depends' : None,
'mode' : 'w' },
{ 'id' : 'target_efficiency',
'description' : """The target efficiency.""",
'type' : 'float',
'default' : 1.0,
'acquisition_base_category' : ('order',),
'acquisition_portal_type' : Expression('python: portal.getPortalAcquisitionMovementTypeList() + portal.getPortalOrderTypeList()'),
'acquisition_copy_value' : 0,
'acquisition_mask_value' : 1,
'acquisition_accessor_id' : 'getTargetEfficiency',
'acquisition_depends' : None,
'mode' : 'w' },
# Profit and loss # Profit and loss
{ 'id' : 'profit_quantity', { 'id' : 'profit_quantity',
'description' : 'A quantity which represents generation of resource from nowhere', 'description' : 'A quantity which represents generation of resource from nowhere',
......
This diff is collapsed.
...@@ -50,23 +50,12 @@ class Inventory: ...@@ -50,23 +50,12 @@ class Inventory:
'acquisition_mask_value' : 1, 'acquisition_mask_value' : 1,
'acquisition_accessor_id' : 'getInventory', 'acquisition_accessor_id' : 'getInventory',
'acquisition_depends' : None, 'acquisition_depends' : None,
'alt_accessor_id' : ('getTargetInventory', ),
'mode' : 'w' }, 'mode' : 'w' },
{ 'id' : 'inventory_efficiency', { 'id' : 'inventory_efficiency',
'description' : """The efficiency of the inventory. 1.0 is perfect.""", 'description' : """The efficiency of the inventory. 1.0 is perfect.""",
'type' : 'float', 'type' : 'float',
'default' : None, 'default' : None,
'mode' : 'w' }, 'mode' : 'w' },
{ 'id' : 'target_inventory',
'description' : """The quantity of items in stock after inventory.""",
'type' : 'float',
'default' : None,
'mode' : 'w' },
{ 'id' : 'target_inventory_efficiency',
'description' : """The efficiency of the inventory. 1.0 is perfect.""",
'type' : 'float',
'default' : None,
'mode' : 'w' },
) )
_categories = () _categories = ()
...@@ -65,12 +65,12 @@ class Predicate: ...@@ -65,12 +65,12 @@ class Predicate:
'mode' : 'w' }, 'mode' : 'w' },
{ 'id' : 'test_method_id', { 'id' : 'test_method_id',
'description' : 'A python method to implement additional tests', 'description' : 'A python method to implement additional tests',
'type' : 'string', 'type' : 'lines', # Only a list of method ids is feasable for lines
'mode' : 'w' },
{ 'id' : 'parameter_string',
'description' : 'A string defining default values for parameters (python syntax)',
'type' : 'string',
'mode' : 'w' }, 'mode' : 'w' },
#{ 'id' : 'parameter_string', # XXX Not feasable for AND
# 'description' : 'A string defining default values for parameters (python syntax)',
# 'type' : 'string',
# 'mode' : 'w' },
# Compatibility with legacy implementation # Compatibility with legacy implementation
# { 'id' : 'predicate_property', # { 'id' : 'predicate_property',
# 'description' : 'The properties to use for the predicate', # 'description' : 'The properties to use for the predicate',
......
...@@ -44,10 +44,10 @@ class CopyToTarget(TargetSolver): ...@@ -44,10 +44,10 @@ class CopyToTarget(TargetSolver):
Adopt values as new target Adopt values as new target
""" """
# Reduce quantity # Reduce quantity
movement.setTargetQuantity(new_target.target_quantity) movement.setQuantity(new_target.target_quantity)
# Change dates # Change dates
movement.setTargetStartDate(new_target.target_start_date) movement.setStartDate(new_target.target_start_date)
movement.setTargetStopDate(new_target.target_stop_date) movement.setStopDate(new_target.target_stop_date)
def close(self): def close(self):
""" """
......
This diff is collapsed.
...@@ -82,9 +82,12 @@ class Test(ERP5TypeTestCase): ...@@ -82,9 +82,12 @@ class Test(ERP5TypeTestCase):
account1 = 'prestation_service' account1 = 'prestation_service'
account2 = 'creance_client' account2 = 'creance_client'
account3 = 'tva_collectee_196' account3 = 'tva_collectee_196'
quantity1 = 3 account1_mirror = 'fourniture_service' # Not used
price1 = 72 account2_mirror = 'dette_client' # Not used
total_price1 = 216 account3_mirror = 'tva_collectee_196' # Not used
quantity1 = 3.0
price1 = 72.0
total_price1 = 216.0
def getTitle(self): def getTitle(self):
return "Invoices" return "Invoices"
...@@ -284,7 +287,7 @@ class Test(ERP5TypeTestCase): ...@@ -284,7 +287,7 @@ class Test(ERP5TypeTestCase):
line1 = order.newContent(portal_type='Sale Order Line',id='1') line1 = order.newContent(portal_type='Sale Order Line',id='1')
product = sequence.get('product') product = sequence.get('product')
line1.setResourceValue(product) line1.setResourceValue(product)
line1.setTargetQuantity(self.quantity1) line1.setQuantity(self.quantity1)
line1.setPrice(self.price1) line1.setPrice(self.price1)
sequence.edit(order=order) sequence.edit(order=order)
self.assertEquals(line1.getTotalPrice(),self.total_price1) self.assertEquals(line1.getTotalPrice(),self.total_price1)
...@@ -321,7 +324,7 @@ class Test(ERP5TypeTestCase): ...@@ -321,7 +324,7 @@ class Test(ERP5TypeTestCase):
rule_line = rule_line_list[0] rule_line = rule_line_list[0]
sequence.edit(order_rule_line=rule_line) sequence.edit(order_rule_line=rule_line)
order_line = order_line_list[0] order_line = order_line_list[0]
self.assertEquals(rule_line.getTargetQuantity(),self.quantity1) self.assertEquals(rule_line.getQuantity(),self.quantity1)
self.assertEquals(rule_line.getPrice(),self.price1) self.assertEquals(rule_line.getPrice(),self.price1)
self.assertEquals(rule_line.getOrderValue(),order_line) self.assertEquals(rule_line.getOrderValue(),order_line)
self.assertEquals(rule_line.getStartDate(),order_line.getStartDate()) self.assertEquals(rule_line.getStartDate(),order_line.getStartDate())
...@@ -342,7 +345,7 @@ class Test(ERP5TypeTestCase): ...@@ -342,7 +345,7 @@ class Test(ERP5TypeTestCase):
rule_line = rule_line_list[0] rule_line = rule_line_list[0]
sequence.edit(invoicing_rule_line=rule_line) sequence.edit(invoicing_rule_line=rule_line)
product = sequence.get('product') product = sequence.get('product')
self.assertEquals(rule_line.getTargetQuantity(),self.quantity1) self.assertEquals(rule_line.getQuantity(),self.quantity1)
self.assertEquals(rule_line.getPrice(),self.price1) self.assertEquals(rule_line.getPrice(),self.price1)
self.assertEquals(rule_line.getPortalType(),'Simulation Movement') self.assertEquals(rule_line.getPortalType(),'Simulation Movement')
self.assertEquals(rule_line.getResourceValue(), product) self.assertEquals(rule_line.getResourceValue(), product)
...@@ -521,6 +524,7 @@ class Test(ERP5TypeTestCase): ...@@ -521,6 +524,7 @@ class Test(ERP5TypeTestCase):
self.assertEquals(transaction_line.getQuantity(), 3 * 72 * 0.5) self.assertEquals(transaction_line.getQuantity(), 3 * 72 * 0.5)
sequence.edit(invoice_transaction_line_receivable=transaction_line) sequence.edit(invoice_transaction_line_receivable=transaction_line)
elif transaction_line.getId() == 'collected_vat' : elif transaction_line.getId() == 'collected_vat' :
simulation_movement = transaction_line.getDeliveryRelatedValueList()[0].getObject()
self.assertEquals(transaction_line.getSourceValue(), self.getAccountModule()['tva_collectee_196']) # this is defined in SaleInvoiceTransaction_init in ERP5 skins. self.assertEquals(transaction_line.getSourceValue(), self.getAccountModule()['tva_collectee_196']) # this is defined in SaleInvoiceTransaction_init in ERP5 skins.
else : else :
raise self.failureException raise self.failureException
...@@ -538,6 +542,7 @@ class Test(ERP5TypeTestCase): ...@@ -538,6 +542,7 @@ class Test(ERP5TypeTestCase):
invoice_transaction_line_receivable = sequence.get('invoice_transaction_line_receivable') invoice_transaction_line_receivable = sequence.get('invoice_transaction_line_receivable')
for transaction_line in payment_transaction_line_list : for transaction_line in payment_transaction_line_list :
if transaction_line.getId() == 'receivable' : if transaction_line.getId() == 'receivable' :
self.assertEquals(transaction_line.getSourceValue(), self.getAccountModule()['creance_client']) self.assertEquals(transaction_line.getSourceValue(), self.getAccountModule()['creance_client'])
self.assertEquals(transaction_line.getQuantity(), 0 - invoice_transaction_line_receivable.getQuantity()) self.assertEquals(transaction_line.getQuantity(), 0 - invoice_transaction_line_receivable.getQuantity())
...@@ -549,6 +554,12 @@ class Test(ERP5TypeTestCase): ...@@ -549,6 +554,12 @@ class Test(ERP5TypeTestCase):
else : else :
raise self.failureException raise self.failureException
# Make sure payment and simulation are consistent
for simulation_movement in transaction_line.getDeliveryRelatedValueList():
self.assertEquals(simulation_movement.getSource(), transaction_line.getSource())
self.assertEquals(simulation_movement.getDestination(), transaction_line.getDestination())
self.assertEquals(simulation_movement.getSourceSection(), transaction_line.getSourceSection())
self.assertEquals(simulation_movement.getDestinationSection(), transaction_line.getDestinationSection())
def testInvoice(self, quiet=0,run=1): def testInvoice(self, quiet=0,run=1):
""" """
......
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