Commit 4cf0d77a authored by Romain Courteaud's avatar Romain Courteaud

slapos_accounting: XXX break instance tree periodicity. Must move to hosting subscription

slapos_accounting: rename script for hosting subscription

slapos_accounting: revert failure error

slapos_accounting: force open order line to have an Instance tree and a hosting subscription

slapos_accounting: hosting subscript will host the date info

slapos_accounting: set open order periodicity on the hosting subscription

slapos_accounting: typo

slapos_accounting: hosting subscription will contain the periodicity

slapos_accounting: create hosting subscription

slapos_accounting: fixup start/stop date confusion

slapos_accounting: fixup

slapos_cloud: add hosting subscription workflow

slapos_cloud: stop using Instance Tree as Subscription Item

slapos_accounting: move periodicity view on hosting subscription

slapos_erp5: open order line have 2 items now

slapos_cloud: open order line has 2 items now

slapos_accounting: simulation is expanded from Hosting Subscription

slapos_accounting: validate hosting subscription

slapos_accounting: check all aggregate value

slapos_accounting: revert aggregate tester

slapos_accounting: simulate from hosting subscription

slapos_accounting: constraint is on hosting subscription

slapos_accounting: interaction are on hosting subscription

slapos_subscription_request: periodicity is on hosting subscription

slapos_accounting: open order line has 2 aggregate

slapos_cloud: hosting subscription have a workflow again

slapos_cloud: add hosting subscription template

slapos_accounting: do not create open order if it was not allocated

slapos_accounting: script renamed

slapos_cloud: update HS_view
parent 8b386f26
......@@ -8,8 +8,8 @@
<portal_type id="Computer Consumption TioXML File">
<item>SortIndex</item>
</portal_type>
<portal_type id="Instance Tree">
<item>SlapOSAccountingInstanceTreeConstraint</item>
<portal_type id="Hosting Subscription">
<item>SlapOSAccountingHostingSubscriptionConstraint</item>
</portal_type>
<portal_type id="Open Sale Order">
<item>SlapOSAccountingOpenSaleOrderConstraint</item>
......
......@@ -11,6 +11,10 @@
<type>Computer Consumption TioXML File</type>
<workflow>document_conversion_interaction_workflow, document_publication_workflow, edit_workflow</workflow>
</chain>
<chain>
<type>Hosting Subscription</type>
<workflow>slapos_accounting_interaction_workflow</workflow>
</chain>
<chain>
<type>Instance Tree</type>
<workflow>slapos_accounting_interaction_workflow, slapos_api_invoicing_workflow</workflow>
......
......@@ -32,7 +32,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SlapOSAccountingInstanceTreeConstraint</string> </value>
<value> <string>SlapOSAccountingHostingSubscriptionConstraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Category Membership Arity Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>constraint_base_category</string> </key>
<value>
<tuple>
<string>aggregate</string>
</tuple>
</value>
</item>
<item>
<key> <string>constraint_portal_type</string> </key>
<value> <string>python: (\'Hosting Subscription\',)</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>aggregate_hosting_subscription_existence_constraint</string> </value>
</item>
<item>
<key> <string>max_arity</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>min_arity</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Category Membership Arity Constraint</string> </value>
</item>
<item>
<key> <string>use_acquisition</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Category Membership Arity Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>constraint_base_category</string> </key>
<value>
<tuple>
<string>aggregate</string>
</tuple>
</value>
</item>
<item>
<key> <string>constraint_portal_type</string> </key>
<value> <string>python: (\'Instance Tree\',)</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>aggregate_instance_tree_existence_constraint</string> </value>
</item>
<item>
<key> <string>max_arity</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>min_arity</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Category Membership Arity Constraint</string> </value>
</item>
<item>
<key> <string>use_acquisition</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
from erp5.component.module.DateUtils import getClosestDate
hosting_subscription = context
assert hosting_subscription.getPortalType() == "Hosting Subscription"
return getClosestDate(target_date=hosting_subscription.getCreationDate(), precision='day')
......@@ -62,7 +62,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>InstanceTree_calculateSubscriptionStartDate</string> </value>
<value> <string>HostingSubscription_calculateSubscriptionStartDate</string> </value>
</item>
</dictionary>
</pickle>
......
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
from erp5.component.module.DateUtils import getClosestDate
instance_tree = context
portal = context.getPortalObject()
workflow_item_list = portal.portal_workflow.getInfoFor(
ob=instance_tree,
name='history',
wf_id='instance_slap_interface_workflow')
start_date = None
for item in workflow_item_list:
start_date = item.get('time')
if start_date:
break
if start_date is None:
# Compatibility with old Instance tree
start_date = instance_tree.getCreationDate()
start_date = getClosestDate(target_date=start_date, precision='day')
return start_date
......@@ -62,9 +62,11 @@ if instance_tree.getCausalityState() == 'diverged':
if (open_order_line is not None) and (open_order_line.getValidationState() == "invalidated"):
instance_tree.converge(comment="Last open order: %s" % open_order_line.getRelativeUrl())
elif open_order_line is None:
# User has no Open Sale Order (likely), so we add the line to remove later. This allow us to charge
# eventual usage between the runs of the alarm.
is_open_order_creation_needed = True
# User has no Open Sale Order (likely).
# No need to charge, as it was never allocated
is_open_order_creation_needed = False
instance_tree.converge(comment="No open order needed as it was never allocated")
elif open_order_line is None:
# Let's add
is_open_order_creation_needed = True
......@@ -77,9 +79,14 @@ if instance_tree.getCausalityState() == 'diverged':
# Add lines
open_sale_order_line_template = portal.restrictedTraverse(
portal.portal_preferences.getPreferredOpenSaleOrderLineTemplate())
open_sale_order_line = open_sale_order_line_template.Base_createCloneDocument(batch_mode=1,
open_order_line = open_sale_order_line_template.Base_createCloneDocument(batch_mode=1,
destination=open_sale_order)
start_date = instance_tree.InstanceTree_calculateSubscriptionStartDate()
hosting_subscription = portal.hosting_subscription_module.newContent(
portal_type="Hosting Subscription",
title=instance_tree.getTitle()
)
hosting_subscription.validate()
start_date = hosting_subscription.HostingSubscription_calculateSubscriptionStartDate()
edit_kw = {}
subscription_request = instance_tree.getAggregateRelatedValue(portal_type="Subscription Request")
......@@ -106,7 +113,7 @@ if instance_tree.getCausalityState() == 'diverged':
# You can increase 0 days to keep generating one month only
# start_date_delta = 0
open_sale_order_line.edit(
open_order_line.edit(
activate_kw=activate_kw,
title=instance_tree.getTitle(),
start_date=start_date,
......@@ -115,19 +122,17 @@ if instance_tree.getCausalityState() == 'diverged':
stop_date=start_date + 1,
# stop_date=calculateOpenOrderLineStopDate(open_sale_order_line,
# instance_tree, start_date_delta=start_date_delta),
aggregate_value=instance_tree,
aggregate_value_list=[hosting_subscription, instance_tree],
**edit_kw
)
storeWorkflowComment(open_sale_order_line, "Created for %s" % instance_tree.getRelativeUrl())
storeWorkflowComment(open_order_line, "Created for %s" % instance_tree.getRelativeUrl())
# instance_tree.converge(comment="Last open order: %s" % open_sale_order_line.getRelativeUrl())
open_order_explanation = "Added %s." % str(open_sale_order_line.getId())
open_order_explanation = "Added %s." % str(open_order_line.getId())
storeWorkflowComment(open_sale_order, open_order_explanation)
else:
open_sale_order = open_order_line.getParentValue()
open_sale_order.OpenSaleOrder_updatePeriod()
if open_order_line is not None:
open_order_line.getParentValue().OpenSaleOrder_updatePeriod()
# Person_storeOpenSaleOrderJournal should fix all divergent Instance Tree in one run
assert instance_tree.getCausalityState() == 'solved'
......@@ -20,14 +20,14 @@ def storeWorkflowComment(document, comment):
portal.portal_workflow.doActionFor(document, 'edit_action', comment=comment)
def calculateOpenOrderLineStopDate(open_order_line, instance_tree, start_date_delta, next_stop_date_delta=0):
def calculateOpenOrderLineStopDate(open_order_line, hosting_subscription, instance_tree, start_date_delta, next_stop_date_delta=0):
end_date = instance_tree.InstanceTree_calculateSubscriptionStopDate()
if end_date is None:
# Be sure that start date is different from stop date
# Consider the first period longer (delta), this allow us to change X days/months
# On a first invoice.
next_stop_date = instance_tree.getNextPeriodicalDate(
instance_tree.InstanceTree_calculateSubscriptionStartDate() + start_date_delta)
next_stop_date = hosting_subscription.getNextPeriodicalDate(
hosting_subscription.HostingSubscription_calculateSubscriptionStartDate() + start_date_delta)
current_stop_date = next_stop_date
# Ensure the invoice is generated 15 days in advance of the next period.
......@@ -35,7 +35,7 @@ def calculateOpenOrderLineStopDate(open_order_line, instance_tree, start_date_de
# Return result should be < now, it order to provide stability in simulation (destruction if it happen should be >= now)
current_stop_date = next_stop_date
next_stop_date = \
instance_tree.getNextPeriodicalDate(current_stop_date)
hosting_subscription.getNextPeriodicalDate(current_stop_date)
return addToDate(current_stop_date, to_add={'second': -1})
else:
......@@ -56,8 +56,9 @@ if open_sale_order.getValidationState() == 'validated':
assert current_stop_date is not None
assert current_start_date < current_stop_date
hosting_subscription = open_order_line.getAggregateValue(portal_type='Hosting Subscription')
instance_tree = open_order_line.getAggregateValue(portal_type='Instance Tree')
assert current_start_date == instance_tree.InstanceTree_calculateSubscriptionStartDate()
assert current_start_date == hosting_subscription.HostingSubscription_calculateSubscriptionStartDate()
subscription_request = instance_tree.getAggregateRelatedValue(portal_type="Subscription Request")
# Define the start date of the period, this can variates with the time.
......@@ -66,7 +67,7 @@ if open_sale_order.getValidationState() == 'validated':
next_stop_date_delta = 46
# First check if the instance tree has been correctly simulated (this script may run only once per year...)
stop_date = calculateOpenOrderLineStopDate(open_order_line, instance_tree,
stop_date = calculateOpenOrderLineStopDate(open_order_line, hosting_subscription, instance_tree,
start_date_delta=0, next_stop_date_delta=next_stop_date_delta)
if current_stop_date < stop_date:
# Bingo, new subscription to generate
......
......@@ -54,7 +54,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>InstanceTree_getRuleReference</string> </value>
<value> <string>HostingSubscription_getRuleReference</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -11,13 +11,13 @@ from unittest import skip
import transaction
class TestInstanceTree(TestSlapOSConstraintMixin):
class TestHostingSubscription(TestSlapOSConstraintMixin):
# use decrator in order to avoid fixing consistency of new object
@WorkflowMethod.disable
def _createInstanceTree(self):
self.subscription = self.portal.instance_tree_module.newContent(
portal_type='Instance Tree')
self.subscription = self.portal.hosting_subscription_module.newContent(
portal_type='Hosting Subscription')
def afterSetUp(self):
TestSlapOSConstraintMixin.afterSetUp(self)
......
......@@ -9,8 +9,8 @@ class TestSlapOSAccountingInteractionWorkflow(SlapOSTestCaseMixin):
def beforeTearDown(self):
transaction.abort()
def _simulateInstanceTree_calculateSubscriptionStartDate(self, date):
script_name = 'InstanceTree_calculateSubscriptionStartDate'
def _simulateHostingSubscription_calculateSubscriptionStartDate(self, date):
script_name = 'HostingSubscription_calculateSubscriptionStartDate'
if script_name in self.portal.portal_skins.custom.objectIds():
raise ValueError('Precondition failed: %s exists in custom' % script_name)
createZODBPythonScript(self.portal.portal_skins.custom,
......@@ -21,17 +21,17 @@ class TestSlapOSAccountingInteractionWorkflow(SlapOSTestCaseMixin):
return DateTime('%s') """ % date.ISO())
transaction.commit()
def _dropInstanceTree_calculateSubscriptionStartDate(self):
script_name = 'InstanceTree_calculateSubscriptionStartDate'
def _dropHostingSubscription_calculateSubscriptionStartDate(self):
script_name = 'HostingSubscription_calculateSubscriptionStartDate'
if script_name in self.portal.portal_skins.custom.objectIds():
self.portal.portal_skins.custom.manage_delObjects(script_name)
transaction.commit()
def test_InstanceTree_fixConsistency(self,
def test_HostingSubscription_fixConsistency(self,
date=DateTime('2012/01/15'), day=15):
new_id = self.generateNewId()
item = self.portal.instance_tree_module.newContent(
portal_type='Instance Tree',
item = self.portal.hosting_subscription_module.newContent(
portal_type='Hosting Subscription',
title="Subscription %s" % new_id,
reference="TESTSUB-%s" % new_id,
periodicity_hour_list=None,
......@@ -43,48 +43,48 @@ return DateTime('%s') """ % date.ISO())
self.assertEqual(item.getPeriodicityMinute(), None)
self.assertEqual(item.getPeriodicityMonthDay(), None)
self._simulateInstanceTree_calculateSubscriptionStartDate(date)
self._simulateHostingSubscription_calculateSubscriptionStartDate(date)
try:
item.fixConsistency()
finally:
self._dropInstanceTree_calculateSubscriptionStartDate()
self._dropHostingSubscription_calculateSubscriptionStartDate()
self.assertEqual(item.getPeriodicityHourList(), [0])
self.assertEqual(item.getPeriodicityMinuteList(), [0])
self.assertEqual(item.getPeriodicityMonthDay(), day)
def test_InstanceTree_fixConsistency_today_after_28(self):
self.test_InstanceTree_fixConsistency(DateTime('2012/01/29'), 28)
def test_HostingSubscription_fixConsistency_today_after_28(self):
self.test_HostingSubscription_fixConsistency(DateTime('2012/01/29'), 28)
def test_InstanceTree_manageAfter(self):
def test_HostingSubscription_manageAfter(self):
class DummyTestException(Exception):
pass
def verify_fixConsistency_call(self):
# Check that fixConsistency is called on instance tree
if self.getRelativeUrl().startswith('instance_tree_module/'):
if self.getRelativeUrl().startswith('hosting_subscription_module/'):
raise DummyTestException
else:
return self.fixConsistency_call()
# Replace serialize by a dummy method
InstanceTreeClass = self.portal.portal_types.getPortalTypeClass(
'Instance Tree')
InstanceTreeClass.fixConsistency_call = InstanceTreeClass.\
HostingSubscriptionClass = self.portal.portal_types.getPortalTypeClass(
'Hosting Subscription')
HostingSubscriptionClass.fixConsistency_call = HostingSubscriptionClass.\
fixConsistency
InstanceTreeClass.fixConsistency = verify_fixConsistency_call
HostingSubscriptionClass.fixConsistency = verify_fixConsistency_call
try:
# manage_afterAdd
self.assertRaises(
DummyTestException,
self.portal.instance_tree_module.newContent,
portal_type='Instance Tree')
self.portal.hosting_subscription_module.newContent,
portal_type='Hosting Subscription')
# manage_afterClone
self.assertRaises(
DummyTestException,
self.portal.instance_tree_module.\
template_instance_tree.Base_createCloneDocument,
self.portal.hosting_subscription_module.\
template_hosting_subscription.Base_createCloneDocument,
batch_mode=1)
finally:
self.portal.portal_types.resetDynamicDocumentsOnceAtTransactionBoundary()
......
......@@ -417,11 +417,12 @@ class TestDefaultPaymentRule(SlapOSTestCaseMixin):
SimulationMovement.getSimulationState = SimulationMovement\
.original_getSimulationState
class TestInstanceTreeSimulation(SlapOSTestCaseMixin):
class TestHostingSubscriptionSimulation(SlapOSTestCaseMixin):
def _prepare(self):
person = self.portal.person_module.template_member\
.Base_createCloneDocument(batch_mode=1)
self.subscription = self.portal.instance_tree_module\
self.subscription = self.portal.hosting_subscription_module.newContent()
self.instance_tree = self.portal.instance_tree_module\
.template_instance_tree.Base_createCloneDocument(batch_mode=1)
self.initial_date = DateTime('2011/02/16')
stop_date = DateTime('2011/04/16')
......@@ -432,6 +433,7 @@ class TestInstanceTreeSimulation(SlapOSTestCaseMixin):
destination_section=person.getRelativeUrl()
)
self.portal.portal_workflow._jumpToStateFor(self.subscription, 'validated')
self.portal.portal_workflow._jumpToStateFor(self.instance_tree, 'validated')
open_sale_order_template = self.portal.restrictedTraverse(
self.portal.portal_preferences.getPreferredOpenSaleOrderTemplate())
......
......@@ -12,12 +12,20 @@ import time
class TestSlapOSAccounting(SlapOSTestCaseMixin):
def createHostingSubscription(self):
new_id = self.generateNewId()
return self.portal.hosting_subscription_module.newContent(
portal_type='Hosting Subscription',
title="Subscription %s" % new_id,
reference="TESTHS-%s" % new_id,
)
def createInstanceTree(self):
new_id = self.generateNewId()
return self.portal.instance_tree_module.newContent(
portal_type='Instance Tree',
title="Subscription %s" % new_id,
reference="TESTHS-%s" % new_id,
reference="TESTIT-%s" % new_id,
)
def createOpenSaleOrder(self):
......@@ -30,72 +38,45 @@ class TestSlapOSAccounting(SlapOSTestCaseMixin):
@withAbort
def test_HS_calculateSubscriptionStartDate_REQUEST_disallowed(self):
item = self.createInstanceTree()
item = self.createHostingSubscription()
self.assertRaises(
Unauthorized,
item.InstanceTree_calculateSubscriptionStartDate,
item.HostingSubscription_calculateSubscriptionStartDate,
REQUEST={})
@withAbort
def test_HS_calculateSubscriptionStartDate_noWorkflow(self):
item = self.createInstanceTree()
item = self.createHostingSubscription()
item.workflow_history['instance_slap_interface_workflow'] = []
date = item.InstanceTree_calculateSubscriptionStartDate()
date = item.HostingSubscription_calculateSubscriptionStartDate()
self.assertEqual(date, item.getCreationDate().earliestTime())
@withAbort
def test_HS_calculateSubscriptionStartDate_withRequest(self):
item = self.createInstanceTree()
item.workflow_history['instance_slap_interface_workflow'] = [{
item = self.createHostingSubscription()
item.workflow_history['edit_workflow'] = [{
'comment':'Directly request the instance',
'error_message': '',
'actor': 'ERP5TypeTestCase',
'slap_state': 'draft',
'time': DateTime('2012/11/15 11:11'),
'action': 'request_instance'
'action': 'edit'
}]
date = item.InstanceTree_calculateSubscriptionStartDate()
date = item.HostingSubscription_calculateSubscriptionStartDate()
self.assertEqual(date, DateTime('2012/11/15'))
@withAbort
def test_HS_calculateSubscriptionStartDate_withRequestEndOfMonth(self):
item = self.createInstanceTree()
item.workflow_history['instance_slap_interface_workflow'] = [{
item = self.createHostingSubscription()
item.workflow_history['edit_workflow'] = [{
'comment':'Directly request the instance',
'error_message': '',
'actor': 'ERP5TypeTestCase',
'slap_state': 'draft',
'time': DateTime('2012/11/30 11:11'),
'action': 'request_instance'
'action': 'edit'
}]
date = item.InstanceTree_calculateSubscriptionStartDate()
date = item.HostingSubscription_calculateSubscriptionStartDate()
self.assertEqual(date, DateTime('2012/11/30'))
@withAbort
def test_HS_calculateSubscriptionStartDate_withRequestAfterDestroy(self):
item = self.createInstanceTree()
destroy_date = DateTime('2012/10/30 11:11')
request_date = DateTime('2012/11/30 11:11')
item.workflow_history['instance_slap_interface_workflow'] = []
item.workflow_history['instance_slap_interface_workflow'].append({
'comment':'Directly destroy',
'error_message': '',
'actor': 'ERP5TypeTestCase',
'slap_state': 'destroy_requested',
'time': destroy_date,
'action': 'request_destroy'
})
item.workflow_history['instance_slap_interface_workflow'].append({
'comment':'Directly request the instance',
'error_message': '',
'actor': 'ERP5TypeTestCase',
'slap_state': 'draft',
'time': request_date,
'action': 'request_instance'
})
date = item.InstanceTree_calculateSubscriptionStartDate()
self.assertEqual(date, DateTime('2012/10/30'))
@withAbort
def test_HS_calculateSubscriptionStopDate_REQUEST_disallowed(self):
item = self.createInstanceTree()
......
......@@ -22,7 +22,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>interaction_InstanceTree_afterAddClone</string> </value>
<value> <string>interaction_HostingSubscription_afterAddClone</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......@@ -32,7 +32,7 @@
<key> <string>portal_type_filter</string> </key>
<value>
<tuple>
<string>Instance Tree</string>
<string>Hosting Subscription</string>
</tuple>
</value>
</item>
......@@ -46,6 +46,10 @@
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>HostingSubscription_afterAddClone</string> </value>
</item>
<item>
<key> <string>trigger_method_id</string> </key>
<value>
......
......@@ -10,7 +10,7 @@
<key> <string>categories</string> </key>
<value>
<tuple>
<string>before_script/portal_workflow/slapos_accounting_interaction_workflow/script_InstanceTree_fixPeriodicity</string>
<string>before_script/portal_workflow/slapos_accounting_interaction_workflow/script_HostingSubscription_fixPeriodicity</string>
</tuple>
</value>
</item>
......@@ -22,7 +22,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>interaction_InstanceTree_fixConsistency</string> </value>
<value> <string>interaction_HostingSubscription_fixConsistency</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......@@ -32,7 +32,7 @@
<key> <string>portal_type_filter</string> </key>
<value>
<tuple>
<string>Instance Tree</string>
<string>Hosting Subscription</string>
</tuple>
</value>
</item>
......@@ -46,6 +46,10 @@
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>HostingSubscription_fixConsistency</string> </value>
</item>
<item>
<key> <string>trigger_method_id</string> </key>
<value>
......
from erp5.component.module.DateUtils import addToDate, getClosestDate
instance_tree = state_change['object']
hosting_subscription = state_change['object']
edit_kw = {}
if instance_tree.getPeriodicityHour() is None:
if hosting_subscription.getPeriodicityHour() is None:
edit_kw['periodicity_hour_list'] = [0]
if instance_tree.getPeriodicityMinute() is None:
if hosting_subscription.getPeriodicityMinute() is None:
edit_kw['periodicity_minute_list'] = [0]
if instance_tree.getPeriodicityMonthDay() is None:
start_date = instance_tree.InstanceTree_calculateSubscriptionStartDate()
if hosting_subscription.getPeriodicityMonthDay() is None:
start_date = hosting_subscription.HostingSubscription_calculateSubscriptionStartDate()
start_date = getClosestDate(target_date=start_date, precision='day')
while start_date.day() >= 29:
start_date = addToDate(start_date, to_add={'day': -1})
edit_kw['periodicity_month_day_list'] = [start_date.day()]
if edit_kw:
instance_tree.edit(**edit_kw)
hosting_subscription.edit(**edit_kw)
......@@ -60,9 +60,15 @@
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_InstanceTree_fixPeriodicity</string> </value>
<value> <string>script_HostingSubscription_fixPeriodicity</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......
......@@ -5,8 +5,8 @@ Compute Node | jump_to_consumption_report_view
Computer Consumption TioXML File | download
Computer Consumption TioXML File | view
Consumption Document Module | view
Hosting Subscription | periodicity
Instance Tree | jump_to_related_open_order_line
Instance Tree | periodicity
Payment Transaction | related_payzen_event
Person | create_new_cloud_contract
Person | jump_to_cloud_contract
......
Cloud Contract Line | SlapOSCloudContractLineAccounting
Cloud Contract | SlapOSCloudContractAccounting
Computer Consumption TioXML File | SortIndex
Instance Tree | SlapOSAccountingInstanceTreeConstraint
Hosting Subscription | SlapOSAccountingHostingSubscriptionConstraint
Open Sale Order Line | SlapOSAccountingOpenSaleOrderLineConstraint
Open Sale Order | SlapOSAccountingOpenSaleOrderConstraint
Sale Invoice Transaction | SlapOSAccountingSaleInvoiceTransactionConstraint
......
......@@ -4,6 +4,7 @@ Cloud Contract | item_workflow
Computer Consumption TioXML File | document_conversion_interaction_workflow
Computer Consumption TioXML File | document_publication_workflow
Computer Consumption TioXML File | edit_workflow
Hosting Subscription | slapos_accounting_interaction_workflow
Instance Tree | slapos_accounting_interaction_workflow
Instance Tree | slapos_api_invoicing_workflow
Sale Invoice Transaction | slapos_accounting_interaction_workflow
......
InstanceAccountingSynchronisation
SlapOSAccountingOpenSaleOrderLineConstraint
SlapOSAccountingOpenSaleOrderConstraint
SlapOSAccountingInstanceTreeConstraint
SlapOSAccountingHostingSubscriptionConstraint
SlapOSAccountingSaleInvoiceTransactionConstraint
SlapOSAccountingSalePackingListConstraint
SlapOSAccountingSalePackingListLineConstraint
......
......@@ -174,9 +174,7 @@ def HostingSubscription_checkInstanceTreeMigrationConsistency(self, fixit=False)
mod = __import__('erp5.portal_type', globals(), locals(), ['Instance Tree'])
klass = getattr(mod, 'Instance Tree')
if ((getattr(self, 'workflow_history', None) is not None) and
('hosting_subscription_workflow' in self.workflow_history)) or \
(self.__class__ == klass) or \
if (self.__class__ == klass) or \
(self.getProperty('sla_xml', None) is not None) or \
([x for x in self.getCategoryList() if (x.startswith('predecessor/') or
x.startswith('successor/'))]):
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Hosting Subscription" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Delete_objects_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>template_hosting_subscription</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>periodicity_hour</string> </key>
<value>
<tuple>
<int>0</int>
</tuple>
</value>
</item>
<item>
<key> <string>periodicity_minute</string> </key>
<value>
<tuple>
<int>0</int>
</tuple>
</value>
</item>
<item>
<key> <string>periodicity_month_day</string> </key>
<value>
<tuple>
<int>17</int>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Hosting Subscription</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -98,7 +98,7 @@
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>SubscriptionItem</string> </value>
<value> <string>Item</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
......
......@@ -13,7 +13,7 @@
</chain>
<chain>
<type>Hosting Subscription</type>
<workflow>edit_workflow</workflow>
<workflow>edit_workflow, hosting_subscription_workflow</workflow>
</chain>
<chain>
<type>Instance Tree</type>
......
......@@ -94,13 +94,16 @@
<value>
<list>
<string>my_title</string>
<string>my_reference</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
<list>
<string>my_translated_validation_state_title</string>
</list>
</value>
</item>
</dictionary>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>display_width</string>
<string>editable</string>
<string>enabled</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_reference</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_reference</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Reference</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_translated_validation_state_title</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_translated_workflow_state_title</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -633,9 +633,15 @@ class DefaultScenarioMixin(TestSlapOSSecurityMixin):
self.assertEqual(len(instance_tree_list), len(line_list))
self.assertSameSet(
[q.getRelativeUrl() for q in instance_tree_list],
[q.getAggregate() for q in line_list]
[q.getAggregate(portal_type="Instance Tree") for q in line_list]
)
# Every line must have 2 aggregate categories:
# one Instance Tree and one Hosting Subscription
for line in line_list:
self.assertEqual(2, len(line.getAggregateList()))
self.assertEqual(1, len(line.getAggregateList(portal_type="Hosting Subscription")))
validated_open_sale_order_list = [q for q in open_sale_order_list
if q.getValidationState() == 'validated']
# if no line, all open orders are kept archived
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>source/portal_workflow/hosting_subscription_workflow/state_draft</string>
</tuple>
</value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>instance_tree_workflow</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>hosting_subscription_workflow</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>manager_bypass</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow</string> </value>
</item>
<item>
<key> <string>state_variable</string> </key>
<value> <string>validation_state</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Hosting Subscription Workflow</string> </value>
</item>
<item>
<key> <string>workflow_managed_permission</string> </key>
<value>
<tuple>
<string>Access contents information</string>
<string>View</string>
<string>Add portal content</string>
<string>Modify portal content</string>
<string>Delete objects</string>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow State" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>acquire_permission</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>state_archived</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow State</string> </value>
</item>
<item>
<key> <string>state_permission_role_list_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>state_type</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Archived</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>Access contents information</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Add portal content</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Delete objects</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Modify portal content</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>View</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow State" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>acquire_permission</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>destination/portal_workflow/hosting_subscription_workflow/transition_validate</string>
<string>destination/portal_workflow/hosting_subscription_workflow/transition_validate_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>state_draft</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow State</string> </value>
</item>
<item>
<key> <string>state_permission_role_list_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>state_type</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Draft</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>Access contents information</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>Add portal content</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>Delete objects</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>Modify portal content</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>View</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow State" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>acquire_permission</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>destination/portal_workflow/hosting_subscription_workflow/transition_archive</string>
<string>destination/portal_workflow/hosting_subscription_workflow/transition_archive_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>state_validated</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow State</string> </value>
</item>
<item>
<key> <string>state_permission_role_list_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>state_type</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Validated</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>Access contents information</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Add portal content</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Delete objects</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Modify portal content</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>View</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Transition" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>action_name</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/workflow</string>
<string>destination/portal_workflow/hosting_subscription_workflow/state_archived</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>guard_permission</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_archive</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Transition</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Archive</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Transition" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string encoding="cdata"><![CDATA[
%(content_url)s/BaseWorkflow_viewWorkflowActionDialog?workflow_action=archive_action&cancel_url=%(content_url)s
]]></string> </value>
</item>
<item>
<key> <string>action_name</string> </key>
<value> <string>Archive</string> </value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/workflow</string>
<string>after_script/portal_workflow/hosting_subscription_workflow/transition_archive</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>guard_group</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>guard_permission</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>guard_role</string> </key>
<value>
<tuple>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_archive_action</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Transition</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ArchiveAction</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Transition" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>action_name</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/workflow</string>
<string>destination/portal_workflow/hosting_subscription_workflow/state_validated</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>guard_permission</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_validate</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Transition</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Validate</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Transition" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string encoding="cdata"><![CDATA[
%(content_url)s/BaseWorkflow_viewWorkflowActionDialog?workflow_action=validate_action&cancel_url=%(content_url)s
]]></string> </value>
</item>
<item>
<key> <string>action_name</string> </key>
<value> <string>Validate</string> </value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/workflow</string>
<string>after_script/portal_workflow/hosting_subscription_workflow/transition_validate</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>guard_group</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>guard_permission</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>guard_role</string> </key>
<value>
<tuple>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_validate_action</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Transition</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ValidateAction</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>The last transition</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_action</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
</item>
<item>
<key> <string>status_included</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>variable_default_expression</string> </key>
<value> <string>transition/getReference|nothing</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>The name of the user who performed the last transition</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_actor</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
</item>
<item>
<key> <string>status_included</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>variable_default_expression</string> </key>
<value> <string>user/getUserName</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Comments about the last transition</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_comment</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
</item>
<item>
<key> <string>status_included</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>variable_default_expression</string> </key>
<value> <string>python:state_change.kwargs.get(\'comment\', \'\')</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Error message if validation failed</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_error_message</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
</item>
<item>
<key> <string>status_included</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Provides access to workflow history</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_history</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
</item>
<item>
<key> <string>status_included</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>variable_default_expression</string> </key>
<value> <string>state_change/getHistory</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>portal type (use as filter for worklists)</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_portal_type</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
</item>
<item>
<key> <string>status_included</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Time of the last transition</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_time</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
</item>
<item>
<key> <string>status_included</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>variable_default_expression</string> </key>
<value> <string>state_change/getDateTime</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -5,6 +5,7 @@ compute_node_module/template_compute_node
compute_node_module/template_compute_node/**
computer_model_module/template_computer_model
computer_model_module/template_computer_model/**
hosting_subscription_module/template_hosting_subscription
instance_tree_module/template_instance_tree
person_module/template_member
person_module/template_member/**
......@@ -26,4 +27,4 @@ portal_caches/last_stored_data_cache_factory/volatile_cache_plugin
product_module/compute_node
software_installation_module/template_software_installation
software_instance_module/template_slave_instance
software_instance_module/template_software_instance
\ No newline at end of file
software_instance_module/template_software_instance
......@@ -3,6 +3,7 @@ Compute Node | slapos_cloud_interaction_workflow
Compute Partition | compute_partition_slap_interface_workflow
Computer Network | network_slap_interface_workflow
Hosting Subscription | edit_workflow
Hosting Subscription | hosting_subscription_workflow
Instance Tree | edit_workflow
Instance Tree | instance_slap_interface_workflow
Instance Tree | instance_tree_workflow
......
audit_validation_workflow
compute_node_slap_interface_workflow
compute_partition_slap_interface_workflow
hosting_subscription_workflow
installation_slap_interface_workflow
instance_slap_interface_workflow
instance_tree_workflow
......
......@@ -321,11 +321,18 @@ class TestSlapOSDefaultCRMEscalation(DefaultScenarioMixin):
line_list = open_sale_order.contentValues(
portal_type='Open Sale Order Line')
self.assertEqual(len(instance_tree_list), len(line_list))
self.assertSameSet(
[q.getRelativeUrl() for q in instance_tree_list],
[q.getAggregate() for q in line_list]
[q.getAggregate(portal_type="Instance Tree") for q in line_list]
)
# Every line must have 2 aggregate categories:
# one Instance Tree and one Hosting Subscription
for line in line_list:
self.assertEqual(2, len(line.getAggregateList()))
self.assertEqual(1, len(line.getAggregateList(portal_type="Hosting Subscription")))
def assertAggregatedSalePackingList(self, delivery):
self.assertEqual('delivered', delivery.getSimulationState())
self.assertEqual('solved', delivery.getCausalityState())
......
......@@ -1829,7 +1829,9 @@ return dict(vads_url_already_registered="%s/already_registered" % (payment_trans
# Ensure periodicity is correct
for subscription_request in subscription_request_list:
instance_tree = subscription_request.getAggregateValue()
self.assertEqual(instance_tree.getPeriodicityMonthDay(),
open_order_line = instance_tree.getAggregateRelatedValue(portal_type="Open Sale Order Line")
hosting_subscription = open_order_line.getAggregateValue(portal_type="Hosting Subscription")
self.assertEqual(hosting_subscription.getPeriodicityMonthDay(),
min(DateTime().day(), 28))
self.pinDateTime(DateTime(DateTime().asdatetime() + datetime.timedelta(days=17)))
......
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