Commit 87f66783 authored by Romain Courteaud's avatar Romain Courteaud

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX EXPAND

slapos_accounting:

* generate one open order per instance tree
* create empty contraint property sheet for open order
* only one line per open order
* workaround wrong select returning a result multiple times
* XXX break instance tree periodicity. Must move to hosting subscription
* rename script for hosting subscription
* revert failure error
* force open order line to have an Instance tree and a hosting subscription
* hosting subscript will host the date info
* set open order periodicity on the hosting subscription
* typo
* hosting subscription will contain the periodicity
* create hosting subscription
* fixup start/stop date confusion
* add hosting subscription workflow
* stop using Instance Tree as Subscription Item
* move periodicity view on hosting subscription
* simulation is expanded from Hosting Subscription
* validate hosting subscription
* check all aggregate value
* revert aggregate tester
* simulate from hosting subscription
* constraint is on hosting subscription
* interaction are on hosting subscription
* open order line has 2 aggregate
* do not create open order if it was not allocated
* script renamed

slapos_erp5:

* open order line have 2 items now

slapos_cloud:

* open order line has 2 items now
* hosting subscription have a workflow again
* add hosting subscription template
* update HS_view

slapos_subscription_request:

* periodicity is on hosting subscription
parent 9c5a6155
......@@ -8,8 +8,14 @@
<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>
</portal_type>
<portal_type id="Open Sale Order Line">
<item>SlapOSAccountingOpenSaleOrderLineConstraint</item>
</portal_type>
<portal_type id="Sale Invoice Transaction">
<item>SlapOSAccountingSaleInvoiceTransactionConstraint</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>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Property Sheet" 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>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SlapOSAccountingHostingSubscriptionConstraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Property Sheet</string> </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>
......@@ -32,7 +32,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SlapOSAccountingInstanceTreeConstraint</string> </value>
<value> <string>SlapOSAccountingOpenSaleOrderConstraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES 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>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: len(context.contentValues(portal_type="Open Sale Order Line")) == 1</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>open_sale_order_line_uniq_constraint</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Only a single Open Sale Order Line must exist</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</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/>
</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="Property Sheet" 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>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SlapOSAccountingOpenSaleOrderLineConstraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Property Sheet</string> </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="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
......@@ -2,11 +2,137 @@ from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
if context.getCausalityState() == 'diverged':
from DateTime import DateTime
person = context.getDestinationSectionValue(portal_type="Person")
portal = context.getPortalObject()
instance_tree = context
now = DateTime()
tag = '%s_%s' % (instance_tree.getUid(), script.id)
activate_kw = {'tag': tag}
if portal.portal_activities.countMessageWithTag(tag) > 0:
# nothing to do
return
def storeWorkflowComment(document, comment):
portal.portal_workflow.doActionFor(document, 'edit_action', comment=comment)
def newOpenOrder(open_sale_order):
open_sale_order_template = portal.restrictedTraverse(
portal.portal_preferences.getPreferredOpenSaleOrderTemplate())
open_order_edit_kw = {
'effective_date': DateTime(),
'activate_kw': activate_kw,
'source': open_sale_order_template.getSource(),
'source_section': open_sale_order_template.getSourceSection()
}
if open_sale_order is None:
new_open_sale_order = open_sale_order_template.Base_createCloneDocument(batch_mode=1)
open_order_edit_kw.update({
'destination': person.getRelativeUrl(),
'destination_decision': person.getRelativeUrl(),
'title': "%s SlapOS Subscription" % person.getTitle(),
})
else:
new_open_sale_order = open_sale_order.Base_createCloneDocument(batch_mode=1)
open_sale_order.setExpirationDate(now, activate_kw=activate_kw)
new_open_sale_order.edit(**open_order_edit_kw)
new_open_sale_order.order(activate_kw=activate_kw)
new_open_sale_order.validate(activate_kw=activate_kw)
return new_open_sale_order
if instance_tree.getCausalityState() == 'diverged':
person = instance_tree.getDestinationSectionValue(portal_type="Person")
# Template document does not have person relation
if person is not None:
person.Person_storeOpenSaleOrderJournal()
# Search an existing related open order
open_order_line = portal.portal_catalog.getResultValue(
portal_type='Open Sale Order Line',
default_aggregate_uid=instance_tree.getUid())
is_open_order_creation_needed = False
# Simply check that it has never been simulated
if instance_tree.getSlapState() == 'destroy_requested':
# Line should be deleted
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).
# 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
# Let's create the open order
if is_open_order_creation_needed:
open_sale_order = newOpenOrder(None)
open_order_explanation = ""
# Add lines
open_sale_order_line_template = portal.restrictedTraverse(
portal.portal_preferences.getPreferredOpenSaleOrderLineTemplate())
open_order_line = open_sale_order_line_template.Base_createCloneDocument(batch_mode=1,
destination=open_sale_order)
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")
# Define the start date of the period, this can variates with the time.
# start_date_delta = 0
if subscription_request is not None:
# Copy from Subscription Condition the source and Source Section into the line
# RAFAEL: As the model is use single Open Order, it isn't possible to use multiple
# companies per region, so we rely on Subscription Conditions to Describe the
# providers.
edit_kw["source"] = subscription_request.getSource()
edit_kw["source_section"] = subscription_request.getSourceSection()
# Quantity is double because the first invoice has to
# charge for 2 months
edit_kw['quantity'] = subscription_request.getQuantity()
edit_kw['price'] = subscription_request.getPrice()
edit_kw['price_currency'] = subscription_request.getPriceCurrency()
# While create move the start date to be at least 1 months
# So we can charge 3 months at once
# You can increase 65 days to generate 3 months
# You can increase 32 days to generate 2 months
# You can increase 0 days to keep generating one month only
# start_date_delta = 0
open_order_line.edit(
activate_kw=activate_kw,
title=instance_tree.getTitle(),
start_date=start_date,
# Ensure stop date value is higher than start date
# it will be updated by OpenSaleOrder_updatePeriod
stop_date=start_date + 1,
# stop_date=calculateOpenOrderLineStopDate(open_sale_order_line,
# instance_tree, start_date_delta=start_date_delta),
aggregate_value_list=[hosting_subscription, instance_tree],
**edit_kw
)
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_order_line.getId())
storeWorkflowComment(open_sale_order, open_order_explanation)
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 context.getCausalityState() == 'solved'
assert instance_tree.getCausalityState() == 'solved'
......@@ -2,7 +2,87 @@ from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
if context.getValidationState() == 'validated':
person = context.getDestinationDecisionValue(portal_type="Person")
from erp5.component.module.DateUtils import addToDate
from DateTime import DateTime
portal = context.getPortalObject()
open_sale_order = context
now = DateTime()
tag = '%s_%s' % (open_sale_order.getUid(), script.id)
activate_kw = {'tag': tag}
if portal.portal_activities.countMessageWithTag(tag) > 0:
# nothing to do
return
def storeWorkflowComment(document, comment):
portal.portal_workflow.doActionFor(document, 'edit_action', comment=comment)
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 = 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.
while next_stop_date < now + next_stop_date_delta:
# 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 = \
hosting_subscription.getNextPeriodicalDate(current_stop_date)
return addToDate(current_stop_date, to_add={'second': -1})
else:
stop_date = end_date
return stop_date
if open_sale_order.getValidationState() == 'validated':
person = open_sale_order.getDestinationDecisionValue(portal_type="Person")
if person is not None:
person.Person_storeOpenSaleOrderJournal()
for open_order_line in open_sale_order.contentValues(
portal_type='Open Sale Order Line'):
current_start_date = open_order_line.getStartDate()
current_stop_date = open_order_line.getStopDate()
# Prevent mistakes
assert current_start_date is not None
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 == 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.
next_stop_date_delta = 0
if subscription_request is not None:
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, 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
open_order_line.edit(
stop_date=stop_date,
activate_kw=activate_kw)
storeWorkflowComment(open_order_line,
'Stop date updated to %s' % stop_date)
if instance_tree.getSlapState() == 'destroy_requested':
# Line should be deleted
assert instance_tree.getCausalityState() == 'diverged'
instance_tree.converge(comment="Last open order: %s" % open_order_line.getRelativeUrl())
open_sale_order.archive()
storeWorkflowComment(open_sale_order, "Instance Tree destroyed: %s" % instance_tree.getRelativeUrl())
elif (instance_tree.getCausalityState() == 'diverged'):
instance_tree.converge(comment="Nothing to do on open order.")
......@@ -34,6 +34,10 @@ select_kw.update(
default_aggregate_portal_type=ComplexQuery(NegatedQuery(Query(default_aggregate_portal_type='Compute Node')),
Query(default_aggregate_portal_type=None),logical_operator="OR"),
grouping_reference=None,
# XXX SELECT DISTINCT uses default_aggregate_portal_type as parameter
# leading to return movement with 2 aggregate values twice
# Use group_by to workaround the issue
group_by_list=['uid'],
sort_on=(('modification_date', 'ASC'),) # the highest chance to find movement which can be delivered
)
movement_list = portal.portal_catalog(**select_kw)
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_storeOpenSaleOrderJournal</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -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>
......
......@@ -6,6 +6,8 @@ root_applied_rule = context.getRootAppliedRule()
root_applied_rule_path = root_applied_rule.getPath()
business_link = context.getCausalityValue(portal_type='Business Link')
if business_link is None:
raise ValueError('Movement without business link: %s' % context.getRelativeUrl())
lock_tag = 'build_in_progress_%s_%s' % (business_link.getUid(), root_applied_rule.getUid())
if context.getPortalObject().portal_activities.countMessageWithTag(lock_tag) == 0:
business_link.build(path='%s/%%' % root_applied_rule_path, activate_kw={'tag': tag})
......
......@@ -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())
......
......@@ -34,12 +34,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):
......@@ -82,46 +90,44 @@ class TestSlapOSAccounting(SlapOSTestCaseMixin):
return invoice
@withAbort
def test_IT_calculateSubscriptionStartDate_REQUEST_disallowed(self):
item = self.createInstanceTree()
def test_HS_calculateSubscriptionStartDate_REQUEST_disallowed(self):
item = self.createHostingSubscription()
self.assertRaises(
Unauthorized,
item.InstanceTree_calculateSubscriptionStartDate,
item.HostingSubscription_calculateSubscriptionStartDate,
REQUEST={})
@withAbort
def test_IT_calculateSubscriptionStartDate_noWorkflow(self):
item = self.createInstanceTree()
def test_HS_calculateSubscriptionStartDate_noWorkflow(self):
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_IT_calculateSubscriptionStartDate_withRequest(self):
item = self.createInstanceTree()
item.workflow_history['instance_slap_interface_workflow'] = [{
def test_HS_calculateSubscriptionStartDate_withRequest(self):
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_IT_calculateSubscriptionStartDate_withRequestEndOfMonth(self):
item = self.createInstanceTree()
item.workflow_history['instance_slap_interface_workflow'] = [{
def test_HS_calculateSubscriptionStartDate_withRequestEndOfMonth(self):
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
......@@ -150,7 +156,7 @@ class TestSlapOSAccounting(SlapOSTestCaseMixin):
self.assertEqual(date, DateTime('2012/10/30'))
@withAbort
def test_IT_calculateSubscriptionStopDate_REQUEST_disallowed(self):
def test_HS_calculateSubscriptionStopDate_REQUEST_disallowed(self):
item = self.createInstanceTree()
self.assertRaises(
Unauthorized,
......@@ -843,5 +849,4 @@ class TestSlapOSAccounting(SlapOSTestCaseMixin):
payment_transaction.PaymentTransaction_start()
self.assertEqual("started",
payment_transaction.getSimulationState())
......@@ -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
Sale Packing List Line | SlapOSAccountingSalePackingListLineConstraint
Sale Packing List | SlapOSAccountingSalePackingListConstraint
......
......@@ -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
SlapOSAccountingInstanceTreeConstraint
SlapOSAccountingOpenSaleOrderLineConstraint
SlapOSAccountingOpenSaleOrderConstraint
SlapOSAccountingHostingSubscriptionConstraint
SlapOSAccountingSaleInvoiceTransactionConstraint
SlapOSAccountingSalePackingListConstraint
SlapOSAccountingSalePackingListLineConstraint
......
......@@ -175,9 +175,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>
......@@ -96,7 +96,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>
......
......@@ -98,13 +98,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>
......@@ -601,35 +601,36 @@ class DefaultScenarioMixin(TestSlapOSSecurityMixin):
self.assertEqual(0, len(open_sale_order_list))
return
self.assertEqual(2, len(open_sale_order_list))
self.assertEqual(len(instance_tree_list), len(open_sale_order_list))
archived_open_sale_order_list = [q for q in open_sale_order_list
if q.getValidationState() == 'archived']
self.assertEqual(len(instance_tree_list), len(archived_open_sale_order_list))
archived_open_sale_order_list.sort(key=lambda x: x.getCreationDate())
# Select the first archived
open_sale_order = archived_open_sale_order_list[0]
line_list = []
for open_sale_order in archived_open_sale_order_list:
archived_line_list = open_sale_order.contentValues(
portal_type='Open Sale Order Line')
self.assertEqual(1, len(archived_line_list))
line_list.extend(archived_line_list)
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")))
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
self.assertEqual(len(validated_open_sale_order_list), 0)
latest_open_sale_order = archived_open_sale_order_list[-1]
line_list = latest_open_sale_order.contentValues(
portal_type='Open Sale Order Line')
self.assertEqual(len(line_list), 0)
def findMessage(self, email, body):
for candidate in reversed(self.portal.MailHost.getMessageList()):
if [q for q in candidate[1] if email in q] and body in candidate[2]:
......
<?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/**
......@@ -25,4 +26,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
......
......@@ -294,11 +294,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())
......
......@@ -1629,8 +1629,12 @@ return dict(vads_url_already_registered="%s/already_registered" % (payment_trans
software_type="default",
partition_reference="_test_subscription_scenario_with_existing_user_extra_instance",
)
self.non_subscription_related_instance_amount = 1
# Trigger open order creation
self.portal.portal_alarms.slapos_request_update_instance_tree_open_sale_order.activeSense()
self.tic()
self.login()
self.requestAndCheckInstanceTree(
amount, name, default_email_text)
......@@ -1802,7 +1806,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