Commit e3e391e2 authored by Romain Courteaud's avatar Romain Courteaud

slapos_subscription_request:

* Subscription Request is a ticket like Credential Request
* restore subscription_request_workflow as ticket_workflow
* Subscription Request is not linked to a project
* add Subscription Request Constraint
* new constraint on Subscription Request
* add testSlapOSERP5VirtualMasterSubscriptionRequestScenario
* drop outdated subscription scenarios
* WIP selling project subscription
* forbid Subscription Request which could generate Invoice with a 0 price
* do not allow empty quantity on Subscription Request
* WIP selling virtual master to customer
* improve test Trade Condition
* disable existing Subscription Request alarm
* create Subscription Request from orphaned Instance Tree
* create Open Sale Order from Subscription Request
* drop slapos_subscription_request_process_planned
* drop slapos_subscription_request_process_draft
* drop slapos_subscription_request_process_ordered
* drop slapos_subscription_request_process_confirmed
* drop slapos_subscription_request_process_started
* drop slapos_subscription_request_process_stopped
* drop Alarm_processStoppedSubscriptionRequest
* test slapos_subscription_request_create_from_orphaned_item
* add assert when creating Open Order
* alarm to validate submitted Subscription Request
* create Subscription Requuest manually from a resource
* create Subscription Request for Compute Node
* trigger validation as soon as a Subscription Request is submitted
* decrease slapos_subscription_request_validate_submitted alarm periodicity
* create Subscription Request as soon as a Compute Node is validated
* create Subscription Request as soon as Instance Tree is validated
* use different trade condition for instance and node
* do not generate Subscription for Organisation
  This work must be done later
* constraint: no Organisation as source_section
* constraint: fixup empty price check
* constraint: destination_section is not required
  for accounting less subscription
* stop creating project from subscription request
  It is too complex (as it requires to create Trade Condition, Assignment, Project, etc...), and I don't know for now how to make something generic from it.
  Use a custom action for now.
parent 352b6117
......@@ -8,7 +8,7 @@
<dictionary>
<item>
<key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_processConfirmedSubscriptionRequest</string> </value>
<value> <string>Alarm_createSubscriptionRequestFromOrphanedItem</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
......@@ -26,7 +26,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_subscription_request_process_confirmed</string> </value>
<value> <string>slapos_subscription_request_create_from_orphaned_item</string> </value>
</item>
<item>
<key> <string>periodicity_hour</string> </key>
......@@ -70,7 +70,7 @@
</tuple>
<state>
<tuple>
<float>1357002060.0</float>
<float>1677632460.0</float>
<string>GMT</string>
</tuple>
</state>
......@@ -89,7 +89,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Process Confirmed Subscription Requests</string> </value>
<value> <string>Create Subscription Request from orphaned item</string> </value>
</item>
</dictionary>
</pickle>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Alarm" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_processDraftSubscriptionRequest</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_subscription_request_process_draft</string> </value>
</item>
<item>
<key> <string>periodicity_hour</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_minute</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_minute_frequency</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>periodicity_month</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_month_day</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_start_date</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1357002060.0</float>
<string>GMT</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>periodicity_week</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Alarm</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Process Draft Subscription Requests</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Alarm" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_processOrderedSubscriptionRequest</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_subscription_request_process_ordered</string> </value>
</item>
<item>
<key> <string>periodicity_hour</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_minute</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_minute_frequency</string> </key>
<value> <int>2</int> </value>
</item>
<item>
<key> <string>periodicity_month</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_month_day</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_start_date</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1357002060.0</float>
<string>GMT</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>periodicity_week</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Alarm</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Process Ordered Subscription Requests</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Alarm" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_processStartedSubscriptionRequest</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_subscription_request_process_started</string> </value>
</item>
<item>
<key> <string>periodicity_hour</string> </key>
<value>
<tuple>
<int>3</int>
</tuple>
</value>
</item>
<item>
<key> <string>periodicity_hour_frequency</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>periodicity_minute</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_minute_frequency</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>periodicity_month</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_month_day</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_start_date</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1357002060.0</float>
<string>GMT</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>periodicity_week</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Alarm</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Process Started Subscription Requests</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Alarm" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_processStoppedSubscriptionRequest</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_subscription_request_process_stopped</string> </value>
</item>
<item>
<key> <string>periodicity_hour</string> </key>
<value>
<tuple>
<int>3</int>
</tuple>
</value>
</item>
<item>
<key> <string>periodicity_hour_frequency</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>periodicity_minute</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_minute_frequency</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>periodicity_month</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_month_day</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_start_date</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1357002060.0</float>
<string>GMT</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>periodicity_week</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Alarm</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Process Stopped Subscription Requests</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -8,7 +8,7 @@
<dictionary>
<item>
<key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_processPlannedSubscriptionRequest</string> </value>
<value> <string>Alarm_validateSubmittedSubscriptionRequest</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
......@@ -26,7 +26,11 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_subscription_request_process_planned</string> </value>
<value> <string>slapos_subscription_request_validate_submitted</string> </value>
</item>
<item>
<key> <string>periodicity_day_frequency</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>periodicity_hour</string> </key>
......@@ -42,7 +46,9 @@
</item>
<item>
<key> <string>periodicity_minute_frequency</string> </key>
<value> <int>1</int> </value>
<value>
<none/>
</value>
</item>
<item>
<key> <string>periodicity_month</string> </key>
......@@ -70,7 +76,7 @@
</tuple>
<state>
<tuple>
<float>1357002060.0</float>
<float>1677632460.0</float>
<string>GMT</string>
</tuple>
</state>
......@@ -89,7 +95,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Process Planned Subscription Requests</string> </value>
<value> <string>Validate submitted Subscription Request</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -16,7 +16,7 @@
<item>Person</item>
<item>Price</item>
<item>Reference</item>
<item>SlapOSVirtualMasterSourceProjectConstraint</item>
<item>SlapOSSubscriptionRequestConstraint</item>
<item>Task</item>
<item>Url</item>
<item>VariationRange</item>
......
......@@ -26,7 +26,7 @@
<key> <string>group_list</string> </key>
<value>
<tuple>
<string>item</string>
<string>ticket</string>
</tuple>
</value>
</item>
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>XMLObject</string> </value>
<value> <string>CredentialRequest</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
......
<workflow_chain>
<chain>
<type>Compute Node</type>
<workflow>slapos_subscription_request_interaction_workflow</workflow>
</chain>
<chain>
<type>Email</type>
<workflow>-coordinate_interaction_workflow</workflow>
</chain>
<chain>
<type>Instance Tree</type>
<workflow>slapos_subscription_request_interaction_workflow</workflow>
</chain>
<chain>
<type>Subscription Condition</type>
<workflow>commerce_validation_workflow, edit_workflow</workflow>
</chain>
<chain>
<type>Subscription Request</type>
<workflow>edit_workflow, subscription_request_workflow</workflow>
<workflow>edit_workflow, slapos_subscription_request_interaction_workflow, subscription_request_workflow</workflow>
</chain>
</workflow_chain>
\ No newline at end of file
<?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>SlapOSSubscriptionRequestConstraint</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>destination</string>
</tuple>
</value>
</item>
<item>
<key> <string>constraint_portal_type</string> </key>
<value> <string>python: (\'Organisation\', )</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>destination_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>destination_section</string>
</tuple>
</value>
</item>
<item>
<key> <string>constraint_portal_type</string> </key>
<value> <string>python: (\'Organisation\',)</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>destination_section_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>0</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>
......@@ -2,59 +2,57 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow State" module="erp5.portal_type"/>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>acquire_permission</string> </key>
<key> <string>_identity_criterion</string> </key>
<value>
<tuple/>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<key> <string>_range_criterion</string> </key>
<value>
<tuple>
<string>destination/portal_workflow/subscription_request_workflow/transition_start</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_start_action</string>
</tuple>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
<value> <string>No invoice with price equals 0 must be generated</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>state_confirmed</string> </value>
<key> <string>expression</string> </key>
<value> <string>python: context.getPrice() or (context.getDestinationSection(\'\') in (context.getSourceSection(\'\'), \'\'))</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow State</string> </value>
<key> <string>id</string> </key>
<value> <string>empty_price_constraint</string> </value>
</item>
<item>
<key> <string>state_permission_role_list_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
<key> <string>message_expression_false</string> </key>
<value> <string>Price must not be 0</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>state_type</string> </key>
<key> <string>data</string> </key>
<value>
<tuple/>
<dictionary/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Confirmed</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......
......@@ -2,14 +2,20 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow State" module="erp5.portal_type"/>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>acquire_permission</string> </key>
<key> <string>_identity_criterion</string> </key>
<value>
<tuple/>
<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>
......@@ -19,33 +25,36 @@
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>state_delivered</string> </value>
<key> <string>expression</string> </key>
<value> <string>python: context.getQuantity(0) == 1</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow State</string> </value>
<key> <string>id</string> </key>
<value> <string>empty_quantity_constraint</string> </value>
</item>
<item>
<key> <string>state_permission_role_list_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
<key> <string>message_expression_false</string> </key>
<value> <string>Quantity must not be empty</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>state_type</string> </key>
<key> <string>data</string> </key>
<value>
<tuple/>
<dictionary/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Delivered</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......
......@@ -2,19 +2,33 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
<global name="Category Membership Arity Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionChineseScenario</string> </value>
<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>price_currency</string>
</tuple>
</value>
</item>
<item>
<key> <string>constraint_portal_type</string> </key>
<value> <string>python: (\'Currency\',)</string> </value>
</item>
<item>
<key> <string>description</string> </key>
......@@ -24,39 +38,39 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSubscriptionChineseScenario</string> </value>
<value> <string>price_currency_constraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
<key> <string>max_arity</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
<key> <string>message_arity_not_in_range</string> </key>
<value> <string>Exactly one Currency shall be selected</string> </value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
<key> <string>message_arity_too_small</string> </key>
<value> <string>Exactly one Currency shall be selected</string> </value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
<key> <string>message_arity_with_portal_type_not_in_range</string> </key>
<value> <string>Exactly one Currency shall be selected</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
<key> <string>message_arity_with_portal_type_too_small</string> </key>
<value> <string>Exactly one Currency shall be selected</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
<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>
......@@ -85,40 +99,7 @@
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
<dictionary/>
</value>
</item>
</dictionary>
......
<?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>quantity_unit</string>
</tuple>
</value>
</item>
<item>
<key> <string>constraint_portal_type</string> </key>
<value> <string>python: (\'Category\', )</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>quantity_unit_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>
......@@ -2,61 +2,72 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
<global name="String Attribute Match Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionCDNScenario</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<key> <string>_identity_criterion</string> </key>
<value>
<none/>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<key> <string>_local_properties</string> </key>
<value>
<none/>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>message_property_not_set</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSubscriptionCDNScenario</string> </value>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
<key> <string>constraint_property</string> </key>
<value>
<tuple>
<string>reference</string>
</tuple>
</value>
</item>
<item>
<key> <string>sid</string> </key>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
<key> <string>id</string> </key>
<value> <string>reference_not_empty_constraint</string> </value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
<key> <string>message_attribute_match</string> </key>
<value> <string>Reference must be defined</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
<key> <string>message_no_such_property</string> </key>
<value> <string>Reference must be defined</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
<key> <string>message_property_not_set</string> </key>
<value> <string>Reference must be defined</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>String Attribute Match Constraint</string> </value>
</item>
</dictionary>
</pickle>
......@@ -70,14 +81,7 @@
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
<dictionary/>
</value>
</item>
</dictionary>
......@@ -85,25 +89,14 @@
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<key> <string>data</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
<dictionary/>
</value>
</item>
</dictionary>
......
<?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>resource</string>
</tuple>
</value>
</item>
<item>
<key> <string>constraint_portal_type</string> </key>
<value> <string>python: (\'Service\', \'Software Product\')</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>resource_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>
......@@ -2,25 +2,34 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow State" module="erp5.portal_type"/>
<global name="Category Membership Arity Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>acquire_permission</string> </key>
<key> <string>_identity_criterion</string> </key>
<value>
<tuple/>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<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>destination/portal_workflow/subscription_request_workflow/transition_confirm</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_confirm_action</string>
<string>source</string>
</tuple>
</value>
</item>
<item>
<key> <string>constraint_portal_type</string> </key>
<value> <string>python: (\'Person\',)</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
......@@ -29,27 +38,23 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>state_ordered</string> </value>
<value> <string>source_constraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow State</string> </value>
<key> <string>max_arity</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>state_permission_role_list_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
<key> <string>min_arity</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>state_type</string> </key>
<value>
<tuple/>
</value>
<key> <string>portal_type</string> </key>
<value> <string>Category Membership Arity Constraint</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Ordered</string> </value>
<key> <string>use_acquisition</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
......@@ -69,4 +74,19 @@
</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>source_decision</string>
</tuple>
</value>
</item>
<item>
<key> <string>constraint_portal_type</string> </key>
<value> <string>python: (\'Person\',)</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>source_decision_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>
......@@ -2,25 +2,34 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow State" module="erp5.portal_type"/>
<global name="Category Membership Arity Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>acquire_permission</string> </key>
<key> <string>_identity_criterion</string> </key>
<value>
<tuple/>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<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>destination/portal_workflow/subscription_request_workflow/transition_order</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_order_action</string>
<string>source_section</string>
</tuple>
</value>
</item>
<item>
<key> <string>constraint_portal_type</string> </key>
<value> <string>python: (\'Person\',)</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
......@@ -29,27 +38,23 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>state_planned</string> </value>
<value> <string>source_section_constraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow State</string> </value>
<key> <string>max_arity</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>state_permission_role_list_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
<key> <string>min_arity</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>state_type</string> </key>
<value>
<tuple/>
</value>
<key> <string>portal_type</string> </key>
<value> <string>Category Membership Arity Constraint</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Planned</string> </value>
<key> <string>use_acquisition</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
......@@ -69,4 +74,19 @@
</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>specialise</string>
</tuple>
</value>
</item>
<item>
<key> <string>constraint_portal_type</string> </key>
<value> <string>python: (\'Sale Trade Condition\',)</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>specialise_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>
......@@ -2,55 +2,68 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
<global name="Property Existence Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionScenario</string> </value>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<key> <string>_local_properties</string> </key>
<value>
<none/>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>message_property_not_set</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSubscriptionScenario</string> </value>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
<key> <string>constraint_property</string> </key>
<value>
<tuple>
<string>start_date</string>
</tuple>
</value>
</item>
<item>
<key> <string>sid</string> </key>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
<key> <string>id</string> </key>
<value> <string>start_date_existence_constraint</string> </value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
<key> <string>message_no_such_property</string> </key>
<value> <string>Property start_date must be defined</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
<key> <string>message_property_not_set</string> </key>
<value> <string>Property start_date must be defined</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
<key> <string>portal_type</string> </key>
<value> <string>Property Existence Constraint</string> </value>
</item>
</dictionary>
</pickle>
......@@ -64,14 +77,7 @@
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
<dictionary/>
</value>
</item>
</dictionary>
......@@ -79,25 +85,14 @@
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<key> <string>data</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
<dictionary/>
</value>
</item>
</dictionary>
......
......@@ -2,23 +2,20 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow State" module="erp5.portal_type"/>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>acquire_permission</string> </key>
<key> <string>_identity_criterion</string> </key>
<value>
<tuple/>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<key> <string>_range_criterion</string> </key>
<value>
<tuple>
<string>destination/portal_workflow/subscription_request_workflow/transition_stop</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_stop_action</string>
</tuple>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
......@@ -28,33 +25,36 @@
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>state_started</string> </value>
<key> <string>expression</string> </key>
<value> <string>python: context.getQuantityUnit(\'\').startswith(\'time/month\')</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow State</string> </value>
<key> <string>id</string> </key>
<value> <string>time_quantity_unit_constraint</string> </value>
</item>
<item>
<key> <string>state_permission_role_list_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
<key> <string>message_expression_false</string> </key>
<value> <string>A "time" quantity unit is required</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>state_type</string> </key>
<key> <string>data</string> </key>
<value>
<tuple/>
<dictionary/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Started</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......
portal = context.getPortalObject()
from Products.ZSQLCatalog.SQLCatalog import SimpleQuery, NegatedQuery
# XXX TODO this is really not efficient
# this does not scale with millions of uid
# how to use a left join instead? or a single query with the embedded subquery?
subscribed_uid_list = [x.uid for x in portal.portal_catalog(
portal_type=["Compute Node", "Instance Tree"],
aggregate__related__portal_type="Subscription Request"
)]
sql_kw = {}
if subscribed_uid_list:
sql_kw['subscribed_uid_list'] = NegatedQuery(SimpleQuery(uid=subscribed_uid_list))
return portal.portal_catalog.searchAndActivate(
method_id='Item_createSubscriptionRequest',
portal_type=["Compute Node", "Instance Tree"],
activate_kw={'tag': tag},
**sql_kw
)
"""
# XXX if there is a non Subscription Request with such aggregate link
# it will lead to not creating the Subscription Request
# TODO find a way to check the portal type
select_dict= {'aggregate__related__uid': None}
kw = {}
kw['select_dict'] = select_dict
kw['left_join_list'] = select_dict.keys()
kw.update(select_dict)
portal.portal_catalog.searchAndActivate(
method_id='Item_createSubscriptionRequest',
# Project are created only from UI for now
portal_type=["Instance Tree", "Compute Node"],
activate_kw={'tag': tag},
**kw
)
"""
context.activate(after_tag=tag).getId()
......@@ -54,7 +54,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Alarm_processPlannedSubscriptionRequest</string> </value>
<value> <string>Alarm_createSubscriptionRequestFromOrphanedItem</string> </value>
</item>
</dictionary>
</pickle>
......
from DateTime import DateTime
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Subscription Request",
simulation_state="draft",
method_id="SubscriptionRequest_verifyReservationPaymentTransaction",
activate_kw={tag: tag}
)
context.activate(after_tag=tag).getId()
from DateTime import DateTime
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Subscription Request",
simulation_state="ordered",
method_id="SubscriptionRequest_processOrdered",
activate_kw={tag: tag}
)
context.activate(after_tag=tag).getId()
from DateTime import DateTime
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Subscription Request",
simulation_state="planned",
method_id="SubscriptionRequest_boostrapUserAccount",
activate_kw={tag: tag}
)
context.activate(after_tag=tag).getId()
from DateTime import DateTime
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Subscription Request",
simulation_state="started",
method_id="SubscriptionRequest_processStarted",
activate_kw={tag: tag}
)
context.activate(after_tag=tag).getId()
from DateTime import DateTime
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Subscription Request",
simulation_state="stopped",
method_id="SubscriptionRequest_processStopped",
activity_kw={tag: tag}
)
context.activate(after_tag=tag).getId()
<?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>tag, fixit, params</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Alarm_processStoppedSubscriptionRequest</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from DateTime import DateTime
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Subscription Request",
simulation_state="confirmed",
method_id="SubscriptionRequest_processConfirmed",
activate_kw={tag: tag}
method_id='SubscriptionRequest_validateIfSubmitted',
# Project are created only from UI for now
portal_type=["Subscription Request"],
simulation_state='submitted',
activate_kw={'tag': tag}
)
context.activate(after_tag=tag).getId()
......@@ -54,7 +54,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Alarm_processConfirmedSubscriptionRequest</string> </value>
<value> <string>Alarm_validateSubmittedSubscriptionRequest</string> </value>
</item>
</dictionary>
</pickle>
......
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
item = context
portal = context.getPortalObject()
tag = '%s_%s' % (item.getUid(), script.id)
activate_kw = {'tag': tag}
if 0 < portal.portal_activities.countMessageWithTag(tag):
# nothing to do
return
def storeWorkflowComment(document, comment):
portal_workflow = document.portal_workflow
last_workflow_item = portal_workflow.getInfoFor(ob=document,
name='comment', wf_id='edit_workflow')
if last_workflow_item != comment:
portal_workflow.doActionFor(document, action='edit_action', comment=comment)
# Search an existing related subscription
subscription_request = portal.portal_catalog.getResultValue(
portal_type='Subscription Request',
aggregate__uid=item.getUid()
)
if subscription_request is not None:
return
#################################################################
# Find matching Service
service = None
source_decision_value = None
resource_vcl = []
if item.getPortalType() == 'Instance Tree':
service, software_release, software_type = item.InstanceTree_getSoftwareProduct()
source_decision_value = item.getDestinationSectionValue(portal_type="Person")
if service is not None:
resource_vcl = [
'software_release/%s' % software_release.getRelativeUrl(),
'software_type/%s' % software_type.getRelativeUrl()
]
resource_vcl.sort()
project_value = item.getFollowUpValue(portal_type="Project")
elif item.getPortalType() == 'Compute Node':
service = portal.restrictedTraverse('service_module/slapos_compute_node_subscription')
resource_vcl = None
project_value = item.getFollowUpValue(portal_type="Project")
if project_value is not None:
source_decision_value = project_value.getDestinationValue(portal_type="Person")
else:
raise ValueError('Unsupported portal type: %s (%s)' % (item.getPortalType(), item.getRelativeUrl()))
# service = self.portal.restrictedTraverse('service_module/slapos_virtual_master_subscription')
if service is None:
storeWorkflowComment(item, 'Can not find a matching Service to generate the Subscription Request')
return
if source_decision_value is None:
storeWorkflowComment(item, 'Can not find the person to contact to generate the Subscription Request')
return
try:
subscription_request = service.Resource_createSubscriptionRequest(source_decision_value, resource_vcl, project_value)
except AssertionError, error:
storeWorkflowComment(item, str(error))
return
subscription_request.setAggregateValue(item)
# Prevent concurrent transactions which could create the Subscription Request
item.serialize()
......@@ -50,11 +50,11 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>tag, fixit, params</string> </value>
<value> <string>REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Alarm_processDraftSubscriptionRequest</string> </value>
<value> <string>Item_createSubscriptionRequest</string> </value>
</item>
</dictionary>
</pickle>
......
portal = context.getPortalObject()
resource = context
if subscriber_person_value is None:
raise AssertionError('Can not find a person profile')
source_project_value = None
destination_project_value = None
item = None
if resource.getPortalType() == "Software Product":
source_project_value = project_value
elif resource.getPortalType() == "Service":
if resource.getRelativeUrl() == "service_module/slapos_compute_node_subscription":
if project_value is None:
raise AssertionError('A project is required for %s %s' % (resource.getRelativeUrl(), project_value))
destination_project_value = project_value
elif resource.getRelativeUrl() == "service_module/slapos_virtual_master_subscription":
if project_value is None:
raise AssertionError('Project is required for %s %s' % (resource.getRelativeUrl(), project_value))
item = project_value
else:
raise NotImplementedError('Unsupported resource: %s' % resource.getRelativeUrl())
else:
raise NotImplementedError('Unsupported resource: %s' % resource.getRelativeUrl())
######################################################
# Find Sale Trade Condition and price
# source_section = subscriber_person_value.getCareerSubordination(subscriber_person_value.getRelativeUrl())
source_section = subscriber_person_value.getRelativeUrl()
# Create a temp Sale Order to calculate the real price and find the trade condition
now = DateTime()
module = portal.portal_trash
#aggregate_value_list = []
tmp_sale_order = module.newContent(
portal_type='Sale Order',
temp_object=True,
#effective_date=now+1,
start_date=now,
# Ensure stop date value is higher than start date
# it will be updated by OpenSaleOrder_updatePeriod
# stop_date=now + 2,
destination_value=subscriber_person_value,
destination_section=source_section,
#destination_decision_value=source_decision_value,
destination_project_value=destination_project_value,
source_project_value=source_project_value,
ledger_value=portal.portal_categories.ledger.automated,
# XXX XXX destination_project_value=instance_tree.getFollowUpValue(),
)
tmp_sale_order.SaleOrder_applySaleTradeCondition(batch_mode=1, force=1)
if tmp_sale_order.getSpecialise(None) is None:
raise AssertionError('Can not find a trade condition to generate the Subscription Request')
# If no accounting is needed, no need to check the price
if (tmp_sale_order.getSourceSection(None) == tmp_sale_order.getDestinationSection(None)) or \
(tmp_sale_order.getSourceSection(None) is None):
price = 0
else:
# Add line
tmp_order_line = tmp_sale_order.newContent(
portal_type='Sale Order Line',
temp_object=True,
resource_value=resource,
variation_category_list=variation_category_list,
quantity_unit=resource.getQuantityUnit(),
base_contribution_list=resource.getBaseContributionList(),
use=resource.getUse(),
quantity=1
)
if variation_category_list:
base_id = 'movement'
cell_key = list(tmp_order_line.getCellKeyList(base_id=base_id))[0]
tmp_order_cell = tmp_order_line.newCell(
base_id=base_id,
portal_type='Sale Order Cell',
temp_object=True,
*cell_key
)
tmp_order_cell.edit(
mapped_value_property_list=['price','quantity'],
quantity=1,
predicate_category_list=cell_key,
variation_category_list=cell_key
)
price = tmp_order_cell.getPrice(0)
else:
price = tmp_order_line.getPrice(0)
if not price:
raise AssertionError('Can not find a price to generate the Subscription Request')
subscription_request = portal.subscription_request_module.newContent(
portal_type='Subscription Request',
source_value=subscriber_person_value,
source_section_value=source_section,
source_decision_value=subscriber_person_value,
source_project_value=source_project_value,
start_date=now,
effective_date=now,
resource_value=resource,
variation_category_list=variation_category_list,
aggregate_value=item,
quantity_unit=resource.getQuantityUnit(),
quantity=1,
ledger="automated",
specialise_value=tmp_sale_order.getSpecialiseValue(),
destination=tmp_sale_order.getSource(),
destination_section=tmp_sale_order.getSourceSection(),
destination_project_value=destination_project_value,
price_currency=tmp_sale_order.getPriceCurrency(),
price=price,
# XXX activate_kw=activate_kw
)
"""
if len(subscription_request.checkConsistency()) != 0:
raise NotImplementedError(subscription_request.checkConsistency())
"""
subscription_request.submit()
return subscription_request
<?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>subscriber_person_value, variation_category_list, project_value</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Resource_createSubscriptionRequest</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
portal = context.getPortalObject()
subscription_request = context
assert subscription_request.getAggregate('') != ''
activate_kw = None
#######################################################
# Hosting Subscription
hosting_subscription = portal.hosting_subscription_module.newContent(
portal_type="Hosting Subscription",
title="hosting %s" % subscription_request.getTitle(),
#follow_up_value=instance_tree.getFollowUpValue(),
ledger_value=portal.portal_categories.ledger.automated,
)
hosting_subscription.validate()
#######################################################
# Open Sale Order
start_date = hosting_subscription.HostingSubscription_calculateSubscriptionStartDate()
open_sale_order = portal.open_sale_order_module.newContent(
portal_type="Open Sale Order",
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,
specialise_value=subscription_request.getSpecialiseValue(),
source_value=subscription_request.getDestinationValue(),
source_section_value=subscription_request.getDestinationSectionValue(),
source_decision_value=subscription_request.getDestinationDecisionValue(),
source_project_value=subscription_request.getDestinationProjectValue(),
destination_value=subscription_request.getSourceValue(),
destination_section_value=subscription_request.getSourceSectionValue(),
destination_decision_value=subscription_request.getSourceDecisionValue(),
destination_project_value=subscription_request.getSourceProjectValue(),
ledger_value=portal.portal_categories.ledger.automated,
causality_value=subscription_request,
price_currency_value=subscription_request.getPriceCurrencyValue(),
activate_kw=activate_kw
)
variation_category_list = subscription_request.getVariationCategoryList()
open_order_line = open_sale_order.newContent(
portal_type="Open Sale Order Line",
resource_value=subscription_request.getResourceValue(),
variation_category_list=variation_category_list,
quantity_unit_value=subscription_request.getQuantityUnitValue(),
base_contribution_list=subscription_request.getBaseContributionList(),
use=subscription_request.getUse(),
activate_kw=activate_kw
)
if variation_category_list:
base_id = 'path'
cell_key = list(open_order_line.getCellKeyList(base_id=base_id))[0]
open_order_cell = open_order_line.newCell(
base_id=base_id,
portal_type="Open Sale Order Cell",
*cell_key
)
open_order_cell.edit(
mapped_value_property_list=['price','quantity'],
predicate_category_list=cell_key,
variation_category_list=cell_key,
activate_kw=activate_kw
)
else:
open_order_cell = open_order_line
open_order_cell.edit(
quantity=subscription_request.getQuantity(),
price=subscription_request.getPrice(),
aggregate_value_list=[
hosting_subscription,
subscription_request.getAggregateValue()
],
activate_kw=activate_kw
)
open_sale_order.OpenSaleOrder_updatePeriod()
open_sale_order.plan()
open_sale_order.validate()
return open_sale_order
......@@ -50,11 +50,11 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>tag, fixit, params</string> </value>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Alarm_processStartedSubscriptionRequest</string> </value>
<value> <string>SubscriptionRequest_createOpenSaleOrder</string> </value>
</item>
</dictionary>
</pickle>
......
subscription_request = context
item = subscription_request.getAggregateValue()
if item is None:
resource = subscription_request.getResourceValue()
raise ValueError('Unsupported resource: %s' % resource.getRelativeUrl())
# Use list setter, to ensure it crashes if item is still None
# subscription_request.setAggregateValueList([item])
subscription_request.SubscriptionRequest_createOpenSaleOrder()
subscription_request.validate()
subscription_request.invalidate()
......@@ -50,11 +50,11 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>tag, fixit, params</string> </value>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Alarm_processOrderedSubscriptionRequest</string> </value>
<value> <string>SubscriptionRequest_validateIfSubmitted</string> </value>
</item>
</dictionary>
</pickle>
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2022 Nexedi SA and Contributors. All Rights Reserved.
#
##############################################################################
from erp5.component.test.testSlapOSERP5VirtualMasterScenario import TestSlapOSVirtualMasterScenarioMixin
from DateTime import DateTime
class TestSlapOSVirtualMasterSubscriptionRequestScenario(TestSlapOSVirtualMasterScenarioMixin):
"""
def createAccountableProject(self):
# create a default project
project = self.addProject(is_accountable=True)
self.web_site = self.portal.web_site_module.slapos_master_panel
preference = self.portal.portal_preferences.slapos_default_system_preference
preference.edit(
preferred_subscription_assignment_category_list=[
'function/customer',
'role/client',
'destination_project/%s' % project.getRelativeUrl()
]
)
return project, self.web_site
"""
def createProjectAdministrator(self, project, web_site):
self.logout()
login = 'owner-%s' % self.generateNewId()
self.joinSlapOS(web_site, login)
self.login()
person = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=login).getParentValue()
# first slapos administrator assignment can only be created by
# the erp5 manager
self.addProjectProductionManagerAssignment(person, project)
self.tic()
return person
def createSoftwareProduct(self, project, person):
self.logout()
self.login(person.getUserId())
software_url = self.generateNewSoftwareReleaseUrl()
software_type = 'public type'
return self.addSoftwareProduct(
"instance product", project, software_url, software_type
)
def createPreparedComputeNode(self, project, person, software_product,
release_variation, type_variation):
self.logout()
self.login(person.getUserId())
server_title = 'Server for %s' % project.getReference()
server_id = self.requestComputeNode(server_title, project.getReference())
server = self.portal.portal_catalog.getResultValue(
portal_type='Compute Node', reference=server_id)
"""
self.setAccessToMemcached(server)
self.assertNotEqual(None, server)
self.setServerOpenPublic(server)"""
self.addAllocationSupply("for compute node", server, software_product,
release_variation, type_variation)
# and install some software on them
self.supplySoftware(server, release_variation.getUrlString())
# format the compute_nodes
self.formatComputeNode(server)
return server
def createSoftwareProductSaleSupply(self, software_product, price=9):
self.logout()
# XXX Use accountant account
self.login()
project = software_product.getFollowUpValue()
sale_supply = self.portal.sale_supply_module.newContent(
portal_type="Sale Supply",
destination_project_value=project,
price_currency_value=project.getSpecialiseValue().getPriceCurrencyValue()
)
# XXX Put price in sale supply module
sale_supply.newContent(
portal_type="Sale Supply Line",
base_price=price,
resource_value=software_product
)
sale_supply.validate()
def addSaleManagerAssignment(self, person):
person.newContent(
portal_type='Assignment',
function='function/sale/manager'
).open()
def createSaleAdministrator(self, web_site):
self.logout()
login = 'sale-%s' % self.generateNewId()
self.joinSlapOS(web_site, login)
self.login()
person = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=login).getParentValue()
# first slapos administrator assignment can only be created by
# the erp5 manager
self.addSaleManagerAssignment(person)
# Remove customer project assignment
person.manage_delObjects(ids=[x.getId() for x in person.contentValues(portal_type="Assignment") if x.getFunction() == 'customer'])
self.tic()
return person
def addAccountingManagerAssignment(self, person):
person.newContent(
portal_type='Assignment',
function='function/accounting/manager'
).open()
def createAccountingAdministrator(self, web_site):
self.logout()
login = 'accounting-%s' % self.generateNewId()
self.joinSlapOS(web_site, login)
self.login()
person = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=login).getParentValue()
# first slapos administrator assignment can only be created by
# the erp5 manager
self.addAccountingManagerAssignment(person)
# Remove customer project assignment
person.manage_delObjects(ids=[x.getId() for x in person.contentValues(portal_type="Assignment") if x.getFunction() == 'customer'])
self.tic()
return person
def createCustomer(self, web_site):
self.logout()
login = 'customer-%s' % self.generateNewId()
self.joinSlapOS(web_site, login)
self.login()
person = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=login).getParentValue()
self.tic()
return person
"""
def bootstrapInvoicingScenario(self):
# Done by ERP5 admin?
project, web_site = self.createAccountableProject()
# lets join as slapos administrator, which will own few compute_nodes
owner_person = self.createProjectAdministrator(project, web_site)
# create a software product
software_product, release_variation, type_variation = self.createSoftwareProduct(project, owner_person)
# create compute_nodes and prepare it
compute_node = self.createPreparedComputeNode(project, owner_person, software_product,
release_variation, type_variation)
# define price
self.createSoftwareProductSaleSupply(software_product)
return project, web_site, software_product, release_variation, type_variation, compute_node
"""
def createProjectCustomer(self, web_site):
# join as the another visitor and request software instance on public
# compute_node
self.logout()
login = 'customer-%s' % self.generateNewId()
self.joinSlapOS(web_site, login)
self.login()
person = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=login).getParentValue()
return person, login
def bootstrapSubscriptionRequestScenario(self):
# Done by ERP5 admin?
# XXX XXX to drop. First project is created on UI
self.web_site = self.portal.web_site_module.slapos_master_panel
web_site = self.web_site
sale_person = self.createSaleAdministrator(web_site)
accountant_person = self.createAccountingAdministrator(web_site)
#################################
# Create accounting data
self.logout()
self.login(accountant_person.getUserId())
currency = self.portal.currency_module.newContent(
portal_type='Currency',
reference="test-currency-%s" % self.generateNewId(),
short_title="tc%s" % self.generateNewId(),
base_unit_quantity=0.01
)
currency.validate()
#################################
# Prepare providing free virtual master to sale users
self.logout()
self.login(sale_person.getUserId())
now = DateTime()
seller_organisation = self.portal.organisation_module.newContent(
portal_type="Organisation",
title="test-seller-%s" % self.generateNewId()
)
seller_bank_account = seller_organisation.newContent(
portal_type="Bank Account",
title="test_bank_account_%s" % self.generateNewId()
)
seller_organisation.validate()
sale_trade_condition = self.portal.sale_trade_condition_module.newContent(
portal_type="Sale Trade Condition",
reference='seller_organisation_%s' % seller_organisation.getTitle(),
# XXX hardcoded
specialise="business_process_module/slapos_ultimate_business_process",
source_value=seller_organisation,
source_section_value=seller_organisation,
source_payment_value=seller_bank_account,
price_currency_value=currency,
payment_condition_payment_mode='test-%s' % self.generateNewId()
)
sale_trade_condition.newContent(
portal_type="Trade Model Line",
reference="VAT",
resource="service_module/slapos_tax",
base_application="base_amount/invoicing/taxable",
trade_phase="slapos/tax",
price=0.2,
quantity=1.0,
membership_criterion_base_category=('price_currency',),
membership_criterion_category=('price_currency/%s' % currency.getRelativeUrl(),)
)
sale_trade_condition.validate()
sale_person.setCareerSubordinationValue(seller_organisation)
internal_trade_condition = self.portal.sale_trade_condition_module.newContent(
portal_type="Sale Trade Condition",
reference='internal_for_%s' % seller_organisation.getTitle(),
# XXX hardcoded
specialise_value=sale_trade_condition,
destination_section_value=seller_organisation,
effective_date=now.earliestTime()
)
internal_trade_condition.validate()
self.tic()
return web_site, now, currency, sale_person, seller_organisation, internal_trade_condition
def checkSiteConsistency(self):
self.logout()
for _ in range(20):
self.stepCallAlarmList()
self.tic()
self.login()
self.stepcheckERP5Consistency()
# after accept, an email is send containing the reset link
last_message = self.portal.MailHost._last_message
assert last_message is None, last_message
def checkVirtualMasterSubscription(self, user_person, now):
#################################
# Buy the first project
self.logout()
self.login(user_person.getUserId())
# XXX How to list possible service to sell?
# Search product with `time` unit ?
service = self.portal.restrictedTraverse('service_module/slapos_virtual_master_subscription')
# XXX This is a script like: Product_submitSubscriptionRequest()
source_decision_value = self.portal.portal_membership.getAuthenticatedMember().getUserValue()
source_section_value = source_decision_value.getCareerSubordinationValue(source_decision_value)
source_section_value = source_decision_value.getCareerSubordinationValue(source_decision_value)
# Find trade condition / price
order_portal_type = 'Sale Order'
line_portal_type = 'Sale Order Line'
#cell_portal_type = 'Sale Order Cell'
#base_id = 'movement'
module = self.portal.sale_order_module
temp_order = 1
#aggregate_value_list = []
open_sale_order = module.newContent(
portal_type=order_portal_type,
temp_object=temp_order,
#effective_date=now+1,
start_date=now,
# Ensure stop date value is higher than start date
# it will be updated by OpenSaleOrder_updatePeriod
# stop_date=now + 2,
destination_value=source_decision_value,
destination_section_value=source_section_value,
#destination_decision_value=source_decision_value,
ledger_value=self.portal.portal_categories.ledger.automated,
# XXX XXX destination_project_value=instance_tree.getFollowUpValue(),
)
"""
resource_vcl = [
'software_release/%s' % software_release.getRelativeUrl(),
'software_type/%s' % software_type.getRelativeUrl()
]
resource_vcl.sort()
assert len(resource_vcl) == 2, service
"""
# Add lines
open_order_line = open_sale_order.newContent(
portal_type=line_portal_type,
temp_object=temp_order,
resource_value=service,
#variation_category_list=resource_vcl,
quantity_unit=service.getQuantityUnit(),
base_contribution_list=service.getBaseContributionList(),
use=service.getUse(),
# stop_date=calculateOpenOrderLineStopDate(open_sale_order_line,
# instance_tree, start_date_delta=start_date_delta),
#activate_kw=activate_kw
quantity=1
)
"""
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:
# Quantity is double because the first invoice has to
# charge for 2 months
edit_kw['quantity'] = subscription_request.getQuantity()
"""
"""
cell_key = list(open_order_line.getCellKeyList(base_id=base_id))[0]
open_order_cell = open_order_line.newCell(
base_id=base_id,
portal_type=cell_portal_type,
temp_object=temp_order,
*cell_key
)
open_order_cell.edit(
mapped_value_property_list=['price','quantity'],
quantity=1,
predicate_category_list=cell_key,
variation_category_list=cell_key,
aggregate_value_list=aggregate_value_list,
activate_kw=activate_kw
)
"""
open_sale_order.SaleOrder_applySaleTradeCondition(batch_mode=1, force=1)
subscription_request = self.portal.subscription_request_module.newContent(
portal_type='Subscription Request',
source_value=source_decision_value,
source_section_value=source_section_value,
source_decision_value=source_decision_value,
start_date=now,
effective_date=now,
resource_value=service,
quantity_unit=open_order_line.getQuantityUnit(),
quantity=1,
ledger="automated",
specialise_value=open_sale_order.getSpecialiseValue(),
destination=open_sale_order.getSource(),
destination_section=open_sale_order.getSourceSection(),
price_currency=open_sale_order.getPriceCurrency(),
price=open_order_line.getPrice()
)
"""
invoicing_type=invoicing_type,
number_tag=number_tag,
number_clip=number_clip,
insured_vehicle_count=insured_vehicle_count,
license_plate_number_list=license_plate_number_list,
free_subscription_dict_json=dumps(free_subscription_dict_json),
promotion_code=promotion_code,
member_reference=member_reference,
role=role,
company_title=company_title,
vat_code=vat_code,
social_title=social_title,
first_name=first_name,
last_name=last_name,
date_of_birth=date_of_birth,
default_email_text=default_email_text,
mobile_telephone_telephone_country=mobile_telephone_telephone_country,
mobile_telephone_telephone_number=mobile_telephone_text,
default_telephone_telephone_country=default_telephone_telephone_country,
default_address_zip_code=default_address_zip_code,
default_address_street_address='\n'.join((default_address_text2, default_address_text3, default_address_text1, default_address_text4)),
default_address_city=default_address_city,
default_address_region=default_address_region or None,
language=language,
client_address=request.getClientAddr(),
destination_reference=destination_reference,
accept_opteven_info=accept_opteven_info,
delivery_mode=delivery_mode,
price_list_json=dumps(price_list),
source_reference=tag_reference, # to lookup by catalog
pack_reference=pack_reference,
# Following fields are hidden and autocompleted by DQE result.
# So it is not passed in the parameters and we read it be REQUEST form
default_address_autocompleted=bool(int(request_form.get('field_your_default_address_autocompleted', "0"))),
delivery_address_autocompleted=has_delivery_address and bool(int(request_form.get('field_your_delivery_address_autocompleted', "0"))),
)
"""
if len(subscription_request.checkConsistency()) != 0:
self.tic()
raise NotImplementedError(str(subscription_request.checkConsistency()[0]))
subscription_request.submit()
self.tic()
#payment_transaction = None
self.logout()
self.login()
#################################
# Alarm / Sale Person validate the request
#################################
item = self.portal.project_module.newContent(
portal_type="Project",
title="new title",
)
item.validate()
subscription_trade_condition = subscription_request.getSpecialiseValue()
project_trade_condition = self.portal.sale_trade_condition_module.newContent(
portal_type="Sale Trade Condition",
reference='for_project_%s' % item.getTitle(),
# XXX hardcoded
specialise="business_process_module/slapos_ultimate_business_process",
source_value=subscription_trade_condition.getSourceValue(),
source_section_value=subscription_trade_condition.getSourceSectionValue(),
destination_project_value=item,
effective_date=now.earliestTime(),
price_currency_value=subscription_trade_condition.getPriceCurrencyValue()
)
project_trade_condition.validate()
subscription_request.getSourceDecisionValue().newContent(
portal_type="Assignment",
destination_project_value=item,
function='production/manager'
).open()
hosting_subscription = self.portal.hosting_subscription_module.newContent(
portal_type="Hosting Subscription",
title="hosting %s" % item.getTitle(),
#follow_up_value=instance_tree.getFollowUpValue(),
ledger_value=self.portal.portal_categories.ledger.automated,
)
hosting_subscription.validate()
start_date = hosting_subscription.HostingSubscription_calculateSubscriptionStartDate()
open_sale_order = self.portal.open_sale_order_module.newContent(
portal_type="Open Sale Order",
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,
specialise_value=subscription_request.getSpecialiseValue(),
destination_value=subscription_request.getSourceValue(),
destination_section_value=subscription_request.getSourceSectionValue(),
destination_decision_value=subscription_request.getSourceDecisionValue(),
ledger_value=self.portal.portal_categories.ledger.automated,
causality_value=subscription_request
)
open_sale_order.SaleOrder_applySaleTradeCondition(batch_mode=1)
# Add lines
service = subscription_request.getResourceValue()
open_order_line = open_sale_order.newContent(
portal_type="Open Sale Order Line",
resource_value=service,
#variation_category_list=resource_vcl,
quantity_unit_value=subscription_request.getQuantityUnitValue(),
base_contribution_list=service.getBaseContributionList(),
use=service.getUse(),
# stop_date=calculateOpenOrderLineStopDate(open_sale_order_line,
# instance_tree, start_date_delta=start_date_delta),
#activate_kw=activate_kw
quantity=1,
price=subscription_request.getPrice(),
aggregate_value_list=[
hosting_subscription,
item
]
)
open_sale_order.plan()
open_sale_order.validate()
subscription_request.validate()
"""
payment_transaction = self.portal.accounting_module.newContent(
portal_type='Payment Transaction',
#start_date=context.getStopDate(),
#resource='currency_module/EUR',
#source_section=sanef_fr_relative_url,
#source_payment_value=bank_account,
causality_value=subscription_request,
#specialise_value=direct_debit_mandate,
# destination_section_value=customer,
#destination_payment=direct_debit_mandate.getSourcePayment(),
#payment_mode='direct_debit',
ledger="automated",
created_by_builder=1, # XXX this prevent init script from creating lines.
#activate_kw={'tag':'%s_init' % context.getDestinationReference()}
)
"""
"""
getAccountForUse = context.Base_getAccountForUse
# receivable
payment_transaction.newContent(
id='receivable',
portal_type='Accounting Transaction Line',
quantity=total_price,
source_value=getAccountForUse('asset_receivable_subscriber'),
)
# bank
payment_transaction.newContent(
id='bank',
portal_type='Accounting Transaction Line',
quantity=-total_price,
source_value=getAccountForUse('collection'),
aggregate_value=payment_transaction_group_value,
)
tag = '%s_update' % context.getDestinationReference()
payment_transaction.confirm(activate_kw={'tag': tag})
payment_transaction.activate(after_tag=tag).stop()
"""
#assert payment_transaction is not None
#################################
# And finally, allow customer to buy a virtual master
#################################
# virtual master subscription is NOT PUBLIC (too complex for anonymous?)
"""
customer, _ = self.createProjectCustomer(web_site)
self.logout()
self.login(customer.getUserId())
"""
return subscription_request
def test_virtual_master_sale_subscribe_to_project(self):
_, now, _, sale_person, _, _ = self.bootstrapSubscriptionRequestScenario()
self.tic()
self.checkVirtualMasterSubscription(sale_person, now)
self.checkSiteConsistency()
def test_virtual_master_customer_subscribe_to_project(self):
web_site, now, _, sale_person, _, internal_trade_condition = self.bootstrapSubscriptionRequestScenario()
self.tic()
main_project_subscription = self.checkVirtualMasterSubscription(sale_person, now)
self.logout()
self.login()
self.tic()
project = main_project_subscription.getCausalityRelatedValue(portal_type="Open Sale Order")\
.contentValues(portal_type="Open Sale Order Line")[0]\
.getAggregateValue(portal_type="Project")
# Setup preferences
preference = self.portal.portal_preferences.slapos_default_system_preference
preference.edit(
preferred_subscription_assignment_category_list=[
'function/customer',
'role/client',
'destination_project/%s' % project.getRelativeUrl()
]
)
#################################
# Buy the first project
customer_person = self.createCustomer(web_site)
self.logout()
self.login(sale_person.getUserId())
user_trade_condition = self.portal.sale_trade_condition_module.newContent(
portal_type="Sale Trade Condition",
reference='user_for_%s' % customer_person.getTitle(),
# XXX hardcoded
specialise_value=internal_trade_condition.getSpecialiseValue(),
#source_value=seller_organisation,
#source_section_value=seller_organisation,
destination_value=customer_person,
destination_section_value=customer_person,
effective_date=now.earliestTime(),
#price_currency_value=currency
)
user_trade_condition.validate()
sale_supply = self.portal.sale_supply_module.newContent(
portal_type="Sale Supply",
source_section=internal_trade_condition.getSpecialiseValue().getSourceSection(),
start_date_range_min=now.earliestTime(),
price_currency=internal_trade_condition.getSpecialiseValue().getPriceCurrency()
)
sale_supply.newContent(
portal_type="Sale Supply Line",
base_price=345,
resource="service_module/slapos_virtual_master_subscription"
)
sale_supply.validate()
self.tic()
self.logout()
self.login(customer_person.getUserId())
self.checkVirtualMasterSubscription(customer_person, now)
self.checkSiteConsistency()
......@@ -8,7 +8,7 @@
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionCancellationScenario</string> </value>
<value> <string>testSlapOSERP5VirtualMasterSubscriptionRequestScenario</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
......@@ -24,7 +24,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSubscriptionCancellationScenario</string> </value>
<value> <string>test.erp5.testSlapOSERP5VirtualMasterSubscriptionRequestScenario</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......
......@@ -23,77 +23,42 @@ from erp5.component.test.SlapOSTestCaseMixin import \
class TestSlapOSSubscriptionRequestProcessAlarm(SlapOSTestCaseMixin):
def test_alarm_slapos_subscription_request_process_draft(self):
script_name = "SubscriptionRequest_verifyReservationPaymentTransaction"
alarm = self.portal.portal_alarms.slapos_subscription_request_process_draft
def test_alarm_slapos_subscription_request_create_from_orphaned_item(self):
script_name = "Item_createSubscriptionRequest"
alarm = self.portal.portal_alarms.slapos_subscription_request_create_from_orphaned_item
subscription_request = self.portal.subscription_request_module.newContent(
portal_type='Subscription Request',
title="Test Subscription Request %s" % self.new_id,
reference="TESTSUBSCRIPTIONREQUEST-%s" % self.new_id
#####################################################
# Instance Tree without Subscription Request
instance_tree = self.portal.instance_tree_module.newContent(
portal_type='Instance Tree',
title="Test Instance Tree no subscription %s" % self.new_id
)
self._test_alarm(alarm, instance_tree, script_name)
self._test_alarm(
alarm, subscription_request, script_name)
def test_alarm_slapos_subscription_request_process_planned(self):
script_name = "SubscriptionRequest_boostrapUserAccount"
alarm = self.portal.portal_alarms.slapos_subscription_request_process_planned
subscription_request = self.portal.subscription_request_module.newContent(
portal_type='Subscription Request',
title="Test Subscription Request %s" % self.new_id,
reference="TESTSUBSCRIPTIONREQUEST-%s" % self.new_id
#####################################################
# Instance Tree with Subscription Request
instance_tree = self.portal.instance_tree_module.newContent(
portal_type='Instance Tree',
title="Test Instance Tree no subscription %s" % self.new_id
)
subscription_request.plan()
self._test_alarm(
alarm, subscription_request, script_name)
def test_alarm_slapos_subscription_request_process_ordered(self):
script_name = "SubscriptionRequest_processOrdered"
alarm = self.portal.portal_alarms.slapos_subscription_request_process_ordered
subscription_request = self.portal.subscription_request_module.newContent(
self.portal.subscription_request_module.newContent(
portal_type='Subscription Request',
title="Test Subscription Request %s" % self.new_id,
reference="TESTSUBSCRIPTIONREQUEST-%s" % self.new_id
aggregate_value=instance_tree
)
subscription_request.plan()
subscription_request.order()
self._test_alarm_not_visited(alarm, instance_tree, script_name)
self._test_alarm(
alarm, subscription_request, script_name)
def test_alarm_slapos_subscription_request_process_confirmed(self):
script_name = "SubscriptionRequest_processConfirmed"
alarm = self.portal.portal_alarms.slapos_subscription_request_process_confirmed
subscription_request = self.portal.subscription_request_module.newContent(
portal_type='Subscription Request',
title="Test Subscription Request %s" % self.new_id,
reference="TESTSUBSCRIPTIONREQUEST-%s" % self.new_id
#####################################################
# Instance Tree aggregated to another portal type
instance_tree = self.portal.instance_tree_module.newContent(
portal_type='Instance Tree',
title="Test Instance Tree another portal type %s" % self.new_id
)
subscription_request.plan()
subscription_request.order()
subscription_request.confirm()
self._test_alarm(
alarm, subscription_request, script_name)
def test_alarm_slapos_subscription_request_process_started(self):
script_name = "SubscriptionRequest_processStarted"
alarm = self.portal.portal_alarms.slapos_subscription_request_process_started
subscription_request = self.portal.subscription_request_module.newContent(
portal_type='Subscription Request',
title="Test Subscription Request %s" % self.new_id,
reference="TESTSUBSCRIPTIONREQUEST-%s" % self.new_id
self.portal.sale_packing_list_module.newContent(
portal_type='Sale Packing List',
title="Test Sale Packing List %s" % self.new_id,
).newContent(
portal_type="Sale Packing List Line",
aggregate_value=instance_tree
)
subscription_request.plan()
subscription_request.order()
subscription_request.confirm()
subscription_request.start()
self._test_alarm(
alarm, subscription_request, script_name)
self._test_alarm(alarm, instance_tree, script_name)
......@@ -6,12 +6,6 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionAlarm</string> </value>
......@@ -55,28 +49,13 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</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>
......@@ -89,7 +68,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -98,7 +77,7 @@
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
......
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from erp5.component.test.testSlapOSSubscriptionCDNScenario import TestSlapOSSubscriptionCDNScenarioMixin
from erp5.component.test.SlapOSTestCaseMixin import changeSkin
class TestSlapOSSubscriptionChineseCDNScenarioMixin(TestSlapOSSubscriptionCDNScenarioMixin):
def afterSetUp(self):
self.expected_slapos_organisation = self.expected_zh_slapos_organisation
TestSlapOSSubscriptionCDNScenarioMixin.afterSetUp(self)
self.expected_price_currency = "currency_module/CNY"
self.normal_user = None
self.expected_notification_language = "zh"
self.login()
@changeSkin('Hal')
def _requestSubscription(self, **kw):
return self._requestSubscriptionViaChineseWebsite(**kw)
def createSubscriptionCondition(self, slave=False):
self.subscription_condition = self.createChineseSubscriptionCondition(
slave=slave)
class TestSlapOSSubscriptionCDNChineseScenario(TestSlapOSSubscriptionChineseCDNScenarioMixin):
def test_subscription_scenario_with_single_vm(self):
self._test_subscription_scenario(amount=1)
def test_subscription_with_3_vms_scenario(self):
self._test_subscription_scenario(amount=3)
def test_subscription_scenario_with_reversal_transaction(self):
self._test_subscription_scenario_with_reversal_transaction(amount=1)
def test_two_subscription_scenario(self):
self._test_two_subscription_scenario(amount=1)
def test_subscription_scenario_with_existing_user(self):
self._test_subscription_scenario_with_existing_user(amount=1, language="zh")
def test_subscription_scenario_with_existing_user_with_non_subscription_request(self):
self._test_subscription_scenario_with_existing_user_with_non_subscription_request(amount=1, language="en")
def test_subscription_scenario_with_existing_english_user(self):
# Messages are in chinese, when subscribed via chinese website. Even if the english language is
# english
self._test_subscription_scenario_with_existing_user(amount=1, language="en")
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionCDNChineseScenario</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSubscriptionCDNChineseScenario</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</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>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from erp5.component.test.testSlapOSSubscriptionScenario import TestSlapOSSubscriptionScenarioMixin
class TestSlapOSSubscriptionCDNScenarioMixin(TestSlapOSSubscriptionScenarioMixin):
def afterSetUp(self):
TestSlapOSSubscriptionScenarioMixin.afterSetUp(self)
self.expected_individual_price_without_tax = 5
self.expected_individual_price_with_tax = 6.0
self.expected_reservation_fee = 1.2
self.expected_reservation_fee_without_tax = 1
self.expected_reservation_quantity_tax = 1.0
self.expected_reservation_tax = 0.2
self.expected_price_currency = "currency_module/EUR"
self.expected_zh_individual_price_without_tax = 40
self.expected_zh_individual_price_with_tax = 40
self.expected_zh_reservation_fee = 8.0
self.expected_zh_reservation_fee_without_tax = 8.0
self.expected_zh_reservation_quantity_tax = 8.0
self.expected_zh_reservation_tax = 0
self.resource_variation_reference = "CDN"
self.login()
# Overwrite default Subscription Condition.
self.createSubscriptionCondition(slave=True)
# some preparation
self.logout()
def createPublicServerForAdminUser(self):
subscription_server = TestSlapOSSubscriptionScenarioMixin.createPublicServerForAdminUser(self)
self.login()
contract = self.admin_user.Person_generateCloudContract(batch=True)
if contract.getValidationState() in ["draft", "invalidated"]:
contract.validate()
self.tic()
# now instantiate it on compute_node and set some nice connection dict
self.setServerOpenPublic(subscription_server)
self.login(self.admin_user.getUserId())
self.personRequestInstanceNotReady(
software_release=self.subscription_condition.getUrlString(),
software_type=self.subscription_condition.getSourceReference(),
partition_reference="InstanceForSlave%s" % self.new_id,
project_reference=self.subscription_project.getReference()
)
self.stepCallSlaposAllocateInstanceAlarm()
self.tic()
self.personRequestInstance(
software_release=self.subscription_condition.getUrlString(),
software_type=self.subscription_condition.getSourceReference(),
partition_reference="InstanceForSlave%s" % self.new_id,
project_reference=self.subscription_project.getReference()
)
# now instantiate it on compute_node and set some nice connection dict
self.simulateSlapgridCP(subscription_server)
self.tic()
self.login()
#self.setServerOpenSubscription(subscription_server)
self.setAccessToMemcached(subscription_server)
self.tic()
self.simulateSlapgridCP(subscription_server)
self.logout()
return subscription_server
class TestSlapOSSubscriptionCDNScenario(TestSlapOSSubscriptionCDNScenarioMixin):
def test_subscription_scenario_with_single_vm(self):
self._test_subscription_scenario(amount=1)
def test_subscription_with_3_vms_scenario(self):
self._test_subscription_scenario(amount=3)
def test_subscription_scenario_with_reversal_transaction(self):
self._test_subscription_scenario_with_reversal_transaction(amount=1)
def test_two_subscription_scenario(self):
self._test_two_subscription_scenario(amount=1)
def test_subscription_scenario_with_existing_user(self):
self._test_subscription_scenario_with_existing_user(amount=1, language="zh")
def test_subscription_scenario_with_existing_user_with_non_subscription_request(self):
self._test_subscription_scenario_with_existing_user_with_non_subscription_request(amount=1, language="en")
def test_subscription_scenario_with_existing_chinese_user(self):
# Messages are in chinese, when subscribed via chinese website. Even if the english language is
# english
self._test_subscription_scenario_with_existing_user(amount=1, language="zh")
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from erp5.component.test.testSlapOSSubscriptionScenario import TestSlapOSSubscriptionScenarioMixin
#from erp5.component.test.SlapOSTestCaseMixin import changeSkin
class TestSlapOSSubscriptionCancellationScenario(TestSlapOSSubscriptionScenarioMixin):
def invokeBasicSimulationAlarmList(self):
# stabilise aggregated invoices and expand them
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# update invoices with their tax & discount
self.stepCallSlaposTriggerBuildAlarm()
self.tic()
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# update invoices with their tax & discount transaction lines
self.stepCallSlaposTriggerBuildAlarm()
self.tic()
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# stop the invoices and solve them again
self.stepCallSlaposStopConfirmedAggregatedSaleInvoiceTransactionAlarm()
self.tic()
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
def test_subscription_scenario_reservation_cancellation_scenario(self):
self.subscription_server = self.createPublicServerForAdminUser()
# Call as anonymous... check response?
default_email_text = "abc%s@nexedi.com" % self.new_id
name="ABC %s" % self.new_id
amount = 1
self.logout()
user_input_dict = {
"name": name,
"amount" : amount}
self._requestSubscription(
subscription_reference=self.subscription_condition.getReference(),
user_input_dict=user_input_dict,
email=default_email_text,
confirmation_required=False)
self.login()
# I'm not sure if this is realistic
self.tic()
subscription_request = self.getSubscriptionRequest(
default_email_text, self.subscription_condition)
self.checkDraftSubscriptionRequest(subscription_request,
default_email_text, self.subscription_condition, amount=amount)
### Here all fine, Now let's consider the user never payed.
invoice = subscription_request.getCausalityValue(
portal_type="Sale Invoice Transaction")
self.assertEqual(invoice.getSimulationState(), "confirmed")
self.assertEqual(invoice.getCausalityState(), "building")
# Invoices are not payed!
payment_list = invoice.getCausalityRelatedValueList(portal_type="Payment Transaction")
self.assertEqual(len(payment_list), 1)
payment = payment_list[0]
self.assertEqual(payment.getSimulationState(), "started")
self.assertEqual(subscription_request.getSimulationState(), "draft")
self.invokeBasicSimulationAlarmList()
payment.cancel()
self.invokeBasicSimulationAlarmList()
# Call alarm to check payment and invoice and move foward to planned.
self.stepCallSlaposSubscriptionRequestProcessDraftAlarm()
self.tic()
payment_list = invoice.getCausalityRelatedValueList(portal_type="Payment Transaction")
self.assertEqual(len(payment_list), 1)
payment = payment_list[0]
self.assertEqual(payment.getSimulationState(), "cancelled")
# Alarm is conflicting
self.assertEqual(subscription_request.getSimulationState(), "cancelled")
def test_subscription_scenario_reservation_cancellation_late_alarm_scenario(self):
self.subscription_server = self.createPublicServerForAdminUser()
# Call as anonymous... check response?
default_email_text = "abc%s@nexedi.com" % self.new_id
name="ABC %s" % self.new_id
amount = 1
self.logout()
user_input_dict = {
"name": name,
"amount" : amount}
self._requestSubscription(
subscription_reference=self.subscription_condition.getReference(),
user_input_dict=user_input_dict,
email=default_email_text,
confirmation_required=False)
self.login()
# I'm not sure if this is realistic
self.tic()
subscription_request = self.getSubscriptionRequest(
default_email_text, self.subscription_condition)
self.checkDraftSubscriptionRequest(subscription_request,
default_email_text, self.subscription_condition, amount=amount)
### Here all fine, Now let's consider the user never payed.
invoice = subscription_request.getCausalityValue(
portal_type="Sale Invoice Transaction")
self.assertEqual(invoice.getSimulationState(), "confirmed")
self.assertEqual(invoice.getCausalityState(), "building")
# Invoices are not payed!
payment_list = invoice.getCausalityRelatedValueList(portal_type="Payment Transaction")
self.assertEqual(len(payment_list), 1)
payment = payment_list[0]
self.assertEqual(payment.getSimulationState(), "started")
self.assertEqual(subscription_request.getSimulationState(), "draft")
self.invokeBasicSimulationAlarmList()
# stop the invoices and solve them again
self.stepCallSlaposStopConfirmedAggregatedSaleInvoiceTransactionAlarm()
self.tic()
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
self.invokeBasicSimulationAlarmList()
self.assertEqual(invoice.getSimulationState(), "stopped")
self.assertEqual(invoice.getCausalityState(), "solved")
self.assertEqual(payment.getSimulationState(), "started")
# Cancel Payment and ensure all is cancelled along
payment.cancel()
self.assertEqual(payment.getSimulationState(), "cancelled")
self.tic()
self.assertEqual(invoice.getSimulationState(), "stopped")
self.assertEqual(invoice.getCausalityState(), "solved")
self.invokeBasicSimulationAlarmList()
# Call alarm to check payment and invoice and move foward to planned.
self.stepCallSlaposSubscriptionRequestProcessDraftAlarm()
self.tic()
payment_list = invoice.getCausalityRelatedValueList(portal_type="Payment Transaction")
self.assertEqual(len(payment_list), 1)
payment = payment_list[0]
self.assertEqual(payment.getSimulationState(), "cancelled")
self.assertEqual(invoice.getSimulationState(), "cancelled")
# Alarm is conflicting
self.assertEqual(subscription_request.getSimulationState(), "cancelled")
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from erp5.component.test.testSlapOSSubscriptionScenario import TestSlapOSSubscriptionScenarioMixin
from erp5.component.test.SlapOSTestCaseMixin import changeSkin
class TestSlapOSSubscriptionChineseScenarioMixin(TestSlapOSSubscriptionScenarioMixin):
def afterSetUp(self):
self.expected_slapos_organisation = self.expected_zh_slapos_organisation
TestSlapOSSubscriptionScenarioMixin.afterSetUp(self)
self.expected_price_currency = "currency_module/CNY"
self.normal_user = None
self.expected_notification_language = "zh"
self.login()
@changeSkin('Hal')
def _requestSubscription(self, **kw):
return self._requestSubscriptionViaChineseWebsite(**kw)
def createSubscriptionCondition(self, slave=False):
self.subscription_condition = self.createChineseSubscriptionCondition(
slave=slave)
class TestSlapOSSubscriptionChineseScenario(TestSlapOSSubscriptionChineseScenarioMixin):
def test_subscription_scenario_with_single_vm(self):
self._test_subscription_scenario(amount=1)
def test_subscription_with_3_vms_scenario(self):
self._test_subscription_scenario(amount=3)
def test_subscription_scenario_with_reversal_transaction(self):
self._test_subscription_scenario_with_reversal_transaction(amount=1)
def test_two_subscription_scenario(self):
self._test_two_subscription_scenario(amount=1)
def test_subscription_scenario_with_existing_user(self):
self._test_subscription_scenario_with_existing_user(amount=1, language="zh")
def test_subscription_scenario_with_existing_user_with_non_subscription_request(self):
self._test_subscription_scenario_with_existing_user_with_non_subscription_request(amount=1, language="en")
def test_subscription_scenario_with_existing_english_user(self):
# Messages are in chinese, when subscribed via chinese website. Even if the english language is
# english
self._test_subscription_scenario_with_existing_user(amount=1, language="en")
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from erp5.component.test.testSlapOSSubscriptionScenario import TestSlapOSSubscriptionScenarioMixin
class testSlapOSSubscriptionDualOrganisationScenario(TestSlapOSSubscriptionScenarioMixin):
def afterSetUp(self):
TestSlapOSSubscriptionScenarioMixin.afterSetUp(self)
fr_organisation, zh_organisation = self.redefineAccountingTemplatesonPreferencesWithDualOrganisation()
self.expected_source = fr_organisation.getRelativeUrl()
self.expected_source_section = fr_organisation.getRelativeUrl()
self.expected_zh_reservation_fee = 188.00
self.subscription_condition.getSpecialiseValue().edit(
source=self.expected_source,
source_section=self.expected_source_section,
source_payment=self.expected_source_section + '/bank_account',
)
self.subscription_condition_zh = self.createChineseSubscriptionCondition()
self.expected_zh_source = zh_organisation.getRelativeUrl()
self.expected_zh_source_section = zh_organisation.getRelativeUrl()
self.subscription_condition_zh.getSpecialiseValue().edit(
source=self.expected_zh_source,
source_section=self.expected_zh_source_section,
source_payment=self.expected_zh_source_section + '/bank_account',
)
self.portal.portal_caches.clearAllCache()
self.tic()
def beforeTearDown(self):
TestSlapOSSubscriptionScenarioMixin.beforeTearDown(self)
self.restoreAccountingTemplatesOnPreferences()
self.portal.portal_caches.clearAllCache()
self.tic()
def requestAndCheckDualInstanceTree(self, amount, name,
default_email_text, language_list):
self.logout()
user_input_dict = {
"name": name,
"amount" : amount}
request_kw = dict(
subscription_reference=self.subscription_condition.getReference(),
user_input_dict=user_input_dict,
email=default_email_text,
confirmation_required=False)
all_subscription_requested_list = []
for language in language_list:
if language == "zh":
self._requestSubscriptionViaChineseWebsite(**request_kw)
subscription_condition = self.subscription_condition_zh
expected_price_currency = "currency_module/CNY"
expected_source_section = self.expected_zh_source_section
else:
self._requestSubscription(**request_kw)
subscription_condition = self.subscription_condition
expected_price_currency = "currency_module/EUR"
expected_source_section = self.expected_source_section
self.login()
# I'm not sure if this is realistic
self.tic()
subscription_request_list = self.getSubscriptionRequestList(
default_email_text, subscription_condition)
for subscription_request in subscription_request_list:
self.assertEqual(language,
subscription_request.getLanguage())
trade_condition = subscription_request.getSpecialiseValue().getSpecialiseValue()
self.assertEqual(expected_price_currency,
trade_condition.getPriceCurrency())
self.assertEqual(expected_source_section,
trade_condition.getSourceSection())
self.checkDraftSubscriptionRequest(subscription_request,
default_email_text,
subscription_request.getSpecialiseValue(),
amount=amount)
self.tic()
if subscription_request not in all_subscription_requested_list:
all_subscription_requested_list.append(subscription_request)
self.checkAndPaySubscriptionPayment(all_subscription_requested_list)
self.tic()
# Call alarm to check payment and invoice and move foward to planned.
self.stepCallSlaposSubscriptionRequestProcessDraftAlarm()
self.tic()
for subscription_request in all_subscription_requested_list:
self.checkPlannedSubscriptionRequest(subscription_request,
default_email_text,
subscription_request.getSpecialiseValue())
# Call alarm to mark subscription request as ordered, bootstrap the user
# and check if email is sent, once done move to ordered state.
self.stepCallSlaposSubscriptionRequestProcessPlannedAlarm()
self.tic()
for subscription_request in all_subscription_requested_list:
self.checkOrderedSubscriptionRequest(subscription_request,
default_email_text,
subscription_request.getSpecialiseValue())
# Call alarm to make the request of the instance?
self.stepCallSlaposSubscriptionRequestProcessOrderedAlarm()
self.tic()
# The alarms might be called multiple times for move each step
self.stepCallSlaposSubscriptionRequestProcessOrderedAlarm()
self.tic()
for subscription_request in all_subscription_requested_list:
sale_packing_list_list = self.portal.portal_catalog(
causality_uid = subscription_request.getUid(),
title="Reservation Deduction",
portal_type="Sale Packing List"
)
self.assertEqual(len(sale_packing_list_list), 1)
sale_packing_list = sale_packing_list_list[0]
trade_condition = subscription_request.getSpecialiseValue().getSpecialiseValue()
self.assertEqual(sale_packing_list.getPriceCurrency(),
trade_condition.getPriceCurrency())
self.assertEqual(sale_packing_list.getSpecialise(),
"sale_trade_condition_module/slapos_reservation_refund_trade_condition")
if trade_condition.getPriceCurrency() == "currency_module/CNY":
expected_reservation_fee = self.expected_zh_reservation_fee_without_tax
else:
expected_reservation_fee = self.expected_reservation_fee_without_tax
self.assertEqual(round(sale_packing_list.getTotalPrice(), 2),
-round(expected_reservation_fee*amount, 2))
return all_subscription_requested_list
def _test_subscription_scenario_with_dual_organisation(self, language_list, amount=1, language="en"):
# Call as anonymous... check response?
default_email_text = "abc%s@nexedi.com" % self.new_id
name="ABC %s" % self.new_id
self.login()
self.createNormalUser(default_email_text, name, language, self.subscription_project)
self.tic()
self.subscription_server = self.createPublicServerForAdminUser()
self.login()
# Extra software from zh version
subscription_server_software = self.subscription_condition_zh.getUrlString()
self.supplySoftware(self.subscription_server, subscription_server_software)
self.tic()
self.logout()
subscription_request_list = self.requestAndCheckDualInstanceTree(
amount, name, default_email_text, language_list=language_list)
self._checkSubscriptionDeploymentAndSimulation(
subscription_request_list, default_email_text,
self.subscription_server)
if not self.expected_free_reservation:
for subscription_request in subscription_request_list:
trade_condition = subscription_request.getSpecialiseValue().getSpecialiseValue()
if trade_condition.getPriceCurrency() == "currency_module/CNY":
self.checkAndPayFirstMonthViaWechat(subscription_request)
else:
self.checkAndPayFirstMonth(subscription_request)
self.tic()
self.stepCallSlaposSubscriptionRequestProcessConfirmedAlarm()
self.tic()
for subscription_request in subscription_request_list:
self.assertTrue(
subscription_request.SubscriptionRequest_testPaymentBalance())
self.assertEqual('start_requested',
subscription_request.getAggregateValue().getSlapState())
# On the second loop that email is send and state is moved to started
self.stepCallSlaposSubscriptionRequestProcessConfirmedAlarm()
self.tic()
for subscription_request in subscription_request_list:
self.assertTrue(
subscription_request.SubscriptionRequest_testPaymentBalance())
self.assertEqual('start_requested',
subscription_request.getAggregateValue().getSlapState())
self.checkStartedSubscriptionRequest(subscription_request,
default_email_text,
subscription_request.getSpecialiseValue())
for subscription_request in subscription_request_list:
self.assertEqual(self.normal_user,
subscription_request.getDestinationSectionValue())
if self.skip_destroy_and_check:
return
for subscription_request in subscription_request_list:
self.assertEqual('start_requested',
subscription_request.getAggregateValue().getSlapState())
# Destroy all instances and process
instance_tree = subscription_request.getAggregateValue()
instance_tree.InstanceTree_requestPerson('destroyed')
self.tic()
self.stepCallSlaposSubscriptionRequestProcessStartedAlarm()
self.tic()
self.checkStoppedSubscriptionRequest(subscription_request,
default_email_text,
subscription_request.getSpecialiseValue())
return default_email_text, name
def test_subscription_scenario_with_dual_organisation_en(self):
self._test_subscription_scenario_with_dual_organisation(["en", "zh", "en"], amount=1, language="en")
def test_subscription_scenario_with_dual_organisation_zh(self):
self._test_subscription_scenario_with_dual_organisation(["en", "zh", "zh"], amount=1, language="zh")
def test_subscription_scenario_with_dual_organisation_en_2(self):
self._test_subscription_scenario_with_dual_organisation(["en", "zh"], amount=2, language="en")
def test_subscription_scenario_with_dual_organisation_zh_2(self):
self._test_subscription_scenario_with_dual_organisation(["en", "zh"], amount=2, language="zh")
def test_subscription_scenario_with_dual_organisation_en_only(self):
self._test_subscription_scenario_with_dual_organisation(["en", "en", "en"], amount=1, language="en")
def test_subscription_scenario_with_dual_organisation_zh_only(self):
self._test_subscription_scenario_with_dual_organisation(["zh", "zh", "zh"], amount=1, language="zh")
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionDualOrganisationScenario</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSubscriptionDualOrganisationScenario</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</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>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from erp5.component.test.testSlapOSSubscriptionChineseScenario import TestSlapOSSubscriptionChineseScenarioMixin
class testSlapOSSubscriptionCloudInvitationTokenScenario(TestSlapOSSubscriptionChineseScenarioMixin):
def test_subscription_scenario_with_single_vm_and_empty_invitation(self):
self.cloud_invitation_token = self.makeCloudInvitationToken()
self._test_subscription_scenario(amount=1)
def test_subscription_with_3_vms_scenario_and_empty_invitation(self):
self.cloud_invitation_token = self.makeCloudInvitationToken()
self._test_subscription_scenario(amount=3)
def test_subscription_scenario_with_reversal_transaction_and_empty_invitation(self):
self.cloud_invitation_token = self.makeCloudInvitationToken()
self._test_subscription_scenario_with_reversal_transaction(amount=1)
def test_two_subscription_scenario_and_empty_invitation(self):
self._test_two_subscription_scenario(amount=1)
def test_subscription_scenario_with_existing_user_and_empty_invitation(self):
self.cloud_invitation_token = self.makeCloudInvitationToken()
self._test_subscription_scenario_with_existing_user(amount=1, language="zh")
def test_subscription_scenario_with_existing_user_with_non_subscription_request_with_empty_invitation(self):
self.cloud_invitation_token = self.makeCloudInvitationToken()
self._test_subscription_scenario_with_existing_user_with_non_subscription_request(amount=1, language="en")
def test_subscription_scenario_with_existing_english_user_and_empty_invitation(self):
self.cloud_invitation_token = self.makeCloudInvitationToken()
self._test_subscription_scenario_with_existing_user(amount=1, language="en")
def _init_test_with_valid_invitation(self):
self.expected_reservation_fee = 0.0
self.expected_reservation_fee_without_tax = 0.0
self.expected_reservation_quantity_tax = 0.0
self.expected_reservation_tax = 0.0
self.expected_free_reservation = 1
self.expected_zh_reservation_fee = 0.0
self.expected_zh_reservation_fee_without_tax = 0.0
self.expected_zh_reservation_quantity_tax = 0.0
self.expected_zh_reservation_tax = 0.0
self.expected_zh_free_reservation = 1
self.cloud_invitation_token = self.makeCloudInvitationToken(
max_invoice_delay=99,
max_invoice_credit_eur=900,
max_invoice_credit_cny=90000)
def test_subscription_scenario_with_single_vm_with_invitation(self):
self._init_test_with_valid_invitation()
self._test_subscription_scenario(amount=1)
def test_subscription_with_3_vms_scenario_with_invitation(self):
self._init_test_with_valid_invitation()
self._test_subscription_scenario(amount=3)
def test_subscription_scenario_with_reversal_transaction_with_invitation(self):
self._init_test_with_valid_invitation()
self._test_subscription_scenario_with_reversal_transaction(amount=1)
def test_two_subscription_scenario_with_invitation(self):
self.expected_reservation_fee = 0.0
self.expected_reservation_fee_without_tax = 0.0
self.expected_reservation_quantity_tax = 0.0
self.expected_reservation_tax = 0.0
self.expected_free_reservation = 1
self.expected_zh_reservation_fee = 0.0
self.expected_zh_reservation_fee_without_tax = 0.0
self.expected_zh_reservation_quantity_tax = 0.0
self.expected_zh_reservation_tax = 0.0
self.expected_zh_free_reservation = 1
self._test_two_subscription_scenario(amount=1,
create_invitation=True,
max_invoice_delay=99,
max_invoice_credit_eur=900,
max_invoice_credit_cny=90000
)
def test_subscription_scenario_with_existing_user_with_invitation(self):
self._init_test_with_valid_invitation()
self._test_subscription_scenario_with_existing_user(amount=1, language="zh")
def test_subscription_scenario_with_existing_english_user_with_invitation(self):
self._init_test_with_valid_invitation()
self._test_subscription_scenario_with_existing_user(amount=1, language="en")
def test_subscription_scenario_with_existing_user_with_non_subscription_request_with_token(self):
self._init_test_with_valid_invitation()
self._test_subscription_scenario_with_existing_user_with_non_subscription_request(amount=1, language="en")
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionInvitationTokenChineseScenario</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSubscriptionInvitationTokenChineseScenario</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</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>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from erp5.component.test.testSlapOSSubscriptionScenario import TestSlapOSSubscriptionScenarioMixin
class testSlapOSSubscriptionCloudInvitationTokenScenario(TestSlapOSSubscriptionScenarioMixin):
def test_subscription_scenario_with_single_vm_and_empty_invitation(self):
self.cloud_invitation_token = self.makeCloudInvitationToken()
self._test_subscription_scenario(amount=1)
def test_subscription_with_3_vms_scenario_and_empty_invitation(self):
self.cloud_invitation_token = self.makeCloudInvitationToken()
self._test_subscription_scenario(amount=3)
def test_subscription_scenario_with_reversal_transaction_and_empty_invitation(self):
self.cloud_invitation_token = self.makeCloudInvitationToken()
self._test_subscription_scenario_with_reversal_transaction(amount=1)
def test_two_subscription_scenario_and_empty_invitation(self):
self._test_two_subscription_scenario(amount=1)
def test_subscription_scenario_with_existing_user_and_empty_invitation(self):
self.cloud_invitation_token = self.makeCloudInvitationToken()
self._test_subscription_scenario_with_existing_user(amount=1, language="zh")
def test_subscription_scenario_with_existing_user_with_non_subscription_request_with_empty_invitation(self):
self.cloud_invitation_token = self.makeCloudInvitationToken()
self._test_subscription_scenario_with_existing_user_with_non_subscription_request(amount=1, language="en")
def test_subscription_scenario_with_existing_english_user_and_empty_invitation(self):
self.cloud_invitation_token = self.makeCloudInvitationToken()
self._test_subscription_scenario_with_existing_user(amount=1, language="en")
def _init_test_with_valid_invitation(self):
self.expected_reservation_fee_without_tax = 0.0
self.expected_reservation_fee = 0.0
self.expected_reservation_quantity_tax = 0.0
self.expected_reservation_tax = 0.0
self.expected_free_reservation = 1
self.cloud_invitation_token = self.makeCloudInvitationToken(
max_invoice_delay=99,
max_invoice_credit_eur=900,
max_invoice_credit_cny=900)
def test_subscription_scenario_with_single_vm_with_invitation(self):
self._init_test_with_valid_invitation()
self._test_subscription_scenario(amount=1)
def test_subscription_with_3_vms_scenario_with_invitation(self):
self._init_test_with_valid_invitation()
self._test_subscription_scenario(amount=3)
def test_subscription_scenario_with_reversal_transaction_with_invitation(self):
self._init_test_with_valid_invitation()
self._test_subscription_scenario_with_reversal_transaction(amount=1)
def test_two_subscription_scenario_with_invitation(self):
self.expected_reservation_fee_without_tax = 0.0
self.expected_reservation_fee = 0.0
self.expected_reservation_quantity_tax = 0.0
self.expected_reservation_tax = 0.0
self.expected_free_reservation = 1
self._test_two_subscription_scenario(amount=1,
create_invitation=True,
max_invoice_delay=99,
max_invoice_credit_eur=900,
max_invoice_credit_cny=900
)
def test_subscription_scenario_with_existing_user_with_invitation(self):
self._init_test_with_valid_invitation()
self._test_subscription_scenario_with_existing_user(amount=1, language="zh")
def test_subscription_scenario_with_existing_english_user_with_invitation(self):
self._init_test_with_valid_invitation()
self._test_subscription_scenario_with_existing_user(amount=1, language="en")
def test_subscription_scenario_with_existing_user_with_non_subscription_request_with_token(self):
self._init_test_with_valid_invitation()
self._test_subscription_scenario_with_existing_user_with_non_subscription_request(amount=1, language="en")
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionInvitationTokenScenario</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSubscriptionInvitationTokenScenario</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</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>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from erp5.component.test.testSlapOSSubscriptionChineseScenario import TestSlapOSSubscriptionChineseScenarioMixin
class testSlapOSSubscriptionNewTemplateChineseScenario(TestSlapOSSubscriptionChineseScenarioMixin):
def afterSetUp(self):
TestSlapOSSubscriptionChineseScenarioMixin.afterSetUp(self)
organisation = self.redefineAccountingTemplatesonPreferences(
price_currency="currency_module/CNY")
self.expected_source = organisation.getRelativeUrl()
self.expected_source_section = organisation.getRelativeUrl()
self.subscription_condition.getSpecialiseValue().edit(
source=self.expected_source,
source_section=self.expected_source_section,
source_payment=self.expected_source_section + '/bank_account',
)
self.portal.portal_caches.clearAllCache()
self.tic()
def beforeTearDown(self):
TestSlapOSSubscriptionChineseScenarioMixin.beforeTearDown(self)
self.restoreAccountingTemplatesOnPreferences()
self.portal.portal_caches.clearAllCache()
self.tic()
def test_subscription_scenario_with_single_vm(self):
self._test_subscription_scenario(amount=1)
def test_subscription_with_3_vms_scenario(self):
self._test_subscription_scenario(amount=3)
def test_subscription_scenario_with_reversal_transaction(self):
self._test_subscription_scenario_with_reversal_transaction(amount=1)
def test_two_subscription_scenario(self):
self._test_two_subscription_scenario(amount=1)
def test_subscription_scenario_with_existing_user(self):
self._test_subscription_scenario_with_existing_user(amount=1, language="zh")
def test_subscription_scenario_with_existing_user_with_non_subscription_request(self):
self._test_subscription_scenario_with_existing_user_with_non_subscription_request(amount=1, language="zh")
def test_subscription_scenario_with_existing_english_user(self):
# Messages are in chinese, when subscribed via chinese website. Even if the english language is
# english
self._test_subscription_scenario_with_existing_user(amount=1, language="en")
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionNewTemplateChineseScenario</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSubscriptionNewTemplateChineseScenario</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</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>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from erp5.component.test.testSlapOSSubscriptionScenario import TestSlapOSSubscriptionScenarioMixin
class testSlapOSSubscriptionNewTemplateScenario(TestSlapOSSubscriptionScenarioMixin):
def afterSetUp(self):
TestSlapOSSubscriptionScenarioMixin.afterSetUp(self)
organisation = self.redefineAccountingTemplatesonPreferences()
self.expected_source = organisation.getRelativeUrl()
self.expected_source_section = organisation.getRelativeUrl()
self.subscription_condition.getSpecialiseValue().edit(
source=self.expected_source,
source_section=self.expected_source_section,
source_payment=self.expected_source_section + '/bank_account',
)
self.portal.portal_caches.clearAllCache()
self.tic()
def beforeTearDown(self):
TestSlapOSSubscriptionScenarioMixin.beforeTearDown(self)
self.restoreAccountingTemplatesOnPreferences()
self.portal.portal_caches.clearAllCache()
self.tic()
def test_subscription_scenario_with_single_vm(self):
self._test_subscription_scenario(amount=1)
def test_subscription_with_3_vms_scenario(self):
self._test_subscription_scenario(amount=3)
def test_subscription_scenario_with_reversal_transaction(self):
self._test_subscription_scenario_with_reversal_transaction(amount=1)
def test_two_subscription_scenario(self):
self._test_two_subscription_scenario(amount=1)
def test_subscription_scenario_with_existing_user(self):
self._test_subscription_scenario_with_existing_user(amount=1, language="zh")
def test_subscription_scenario_with_existing_user_with_non_subscription_request(self):
self._test_subscription_scenario_with_existing_user_with_non_subscription_request(amount=1, language="zh")
def test_subscription_scenario_with_existing_english_user(self):
# Messages are in chinese, when subscribed via chinese website. Even if the english language is
# english
self._test_subscription_scenario_with_existing_user(amount=1, language="en")
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionNewTemplateScenario</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSubscriptionNewTemplateScenario</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</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>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from erp5.component.test.testSlapOSSubscriptionScenario import TestSlapOSSubscriptionScenarioMixin
class testSlapOSSubscriptionPerUserTradeConditionScenario(TestSlapOSSubscriptionScenarioMixin):
def createCustomUserSaleTradeCondition(self, person):
root_trade_condition = self.portal.portal_preferences.\
getPreferredAggregatedSubscriptionSaleTradeCondition()
root_trade_condition_value = self.portal.restrictedTraverse(
root_trade_condition)
user_trade_condition = root_trade_condition_value.\
Base_createCloneDocument(batch_mode=1)
user_trade_condition.manage_delObjects([x for x in
user_trade_condition.contentIds()])
user_trade_condition.edit(
title="TEST Trade Condition for %s" % person.getTitle(),
reference="%s_custom_%s" % (user_trade_condition.getReference(), user_trade_condition.getUid()),
destination_section_value=person,
specialise_value=self.subscription_condition.getSpecialiseValue(),
payment_mode='payzen',
price_currency_value=self.subscription_condition.getSpecialiseValue().getPriceCurrency(),
)
user_trade_condition.validate()
self.subscription_condition.edit(specialise_value=user_trade_condition)
return user_trade_condition
def _test_subscription_scenario_with_custom_condition(self, amount=1, language=None):
""" The admin creates an compute_node, user can request instances on it"""
# Call as anonymous... check response?
default_email_text = "abc%s@nexedi.com" % self.new_id
name="ABC %s" % self.new_id
self.login()
self.createNormalUser(default_email_text, name, language, self.subscription_project)
self.createCustomUserSaleTradeCondition(self.normal_user)
self.tic()
self.subscription_server = self.createPublicServerForAdminUser()
self.requestAndCheckInstanceTree(
amount, name, default_email_text)
self.checkSubscriptionDeploymentAndSimulation(
default_email_text, self.subscription_server)
subscription_request = self.getSubscriptionRequest(
default_email_text, self.subscription_condition)
self.assertEqual(self.normal_user,
subscription_request.getDestinationSectionValue())
self.destroyAndCheckSubscription(
default_email_text, self.subscription_server
)
return default_email_text, name
def test_custom_scenario(self):
self._test_subscription_scenario_with_custom_condition(amount=1, language="en")
class testSlapOSSubscriptionPerUserTradeConditionScenarioDetaxed(TestSlapOSSubscriptionScenarioMixin):
def afterSetUp(self):
TestSlapOSSubscriptionScenarioMixin.afterSetUp(self)
self.expected_individual_price_without_tax = 162.5
self.expected_individual_price_with_tax = 195
self.expected_reservation_fee = 25.00
self.expected_reservation_fee_without_tax = 20.83
self.expected_reservation_quantity_tax = 20.83
self.expected_reservation_tax = 4.17
def createDetaxedUserSaleTradeCondition(self, person):
root_trade_condition = self.portal.portal_preferences.\
getPreferredAggregatedSubscriptionSaleTradeCondition()
root_trade_condition_value = self.portal.restrictedTraverse(
root_trade_condition)
user_trade_condition = root_trade_condition_value.\
Base_createCloneDocument(batch_mode=1)
user_trade_condition.edit(
title="TEST Trade Condition for %s" % person.getTitle(),
reference="%s_detaxed_%s" % (user_trade_condition.getReference(), user_trade_condition.getUid()),
destination_section_value=person,
specialise_value=self.subscription_condition.getSpecialiseValue(),
payment_mode='payzen',
price_currency_value=self.subscription_condition.getSpecialiseValue().getPriceCurrency(),
)
user_trade_condition.manage_delObjects([x for x in
user_trade_condition.contentIds()])
user_trade_condition.newContent(
portal_type="Sale Supply Line",
base_price=self.expected_reservation_fee_without_tax,
resource='service_module/slapos_reservation_fee_2',
)
user_trade_condition.newContent(
portal_type="Sale Supply Line",
base_price=self.expected_individual_price_without_tax,
resource='service_module/slapos_instance_subscription',
)
user_trade_condition.validate()
self.subscription_condition.edit(specialise_value=user_trade_condition)
return user_trade_condition
def _test_subscription_scenario_with_detaxed_condition(self, amount=1, language=None):
""" The admin creates an compute_node, user can request instances on it"""
# Call as anonymous... check response?
default_email_text = "abc%s@nexedi.com" % self.new_id
name="ABC %s" % self.new_id
self.login()
self.createNormalUser(default_email_text, name, language, self.subscription_project)
self.createDetaxedUserSaleTradeCondition(self.normal_user)
self.tic()
self.subscription_server = self.createPublicServerForAdminUser()
self.requestAndCheckInstanceTree(
amount, name, default_email_text)
self.checkSubscriptionDeploymentAndSimulation(
default_email_text, self.subscription_server)
subscription_request = self.getSubscriptionRequest(
default_email_text, self.subscription_condition)
self.assertEqual(self.normal_user,
subscription_request.getDestinationSectionValue())
self.destroyAndCheckSubscription(
default_email_text, self.subscription_server
)
return default_email_text, name
def test_detaxed_scenario(self):
self._test_subscription_scenario_with_detaxed_condition(amount=1, language="en")
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionPerUserTradeConditionScenario</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSubscriptionPerUserTradeConditionScenario</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</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>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from erp5.component.test.SlapOSTestCaseDefaultScenarioMixin import DefaultScenarioMixin
from erp5.component.test.SlapOSTestCaseMixin import changeSkin
from Products.ERP5Type.tests.utils import createZODBPythonScript
from DateTime import DateTime
import datetime
class TestSlapOSSubscriptionScenarioMixin(DefaultScenarioMixin):
def afterSetUp(self):
self.unpinDateTime()
self.normal_user = None
# XXX TODO change price to ensure nothing is hardcoded
self.expected_individual_price_without_tax = 195
self.expected_individual_price_with_tax = 234
self.expected_reservation_fee = 30.00
self.expected_reservation_fee_without_tax = 25
self.expected_reservation_quantity_tax = 25
self.expected_reservation_tax = 5.0
self.expected_price_currency = "currency_module/EUR"
self.expected_zh_individual_price_without_tax = 1888
self.expected_zh_individual_price_with_tax = 1888
self.expected_zh_reservation_fee = 188.00
self.expected_zh_reservation_fee_without_tax = 188
self.expected_zh_reservation_quantity_tax = 188
self.expected_zh_reservation_tax = 1.88
# XXX TODO change source to ensure nothing is hardcoded
self.expected_notification_language = "en"
self.expected_source = self.expected_slapos_organisation
self.expected_source_section = self.expected_slapos_organisation
self.cloud_invitation_token = None
self.resource_variation_reference = None
self.expected_free_reservation = 0
self.non_subscription_related_instance_amount = 0
self.skip_destroy_and_check = 0
self.login()
self.portal.portal_alarms.slapos_subscription_request_process_draft.setEnabled(True)
self.portal.portal_alarms.slapos_subscription_request_process_ordered.setEnabled(True)
self.portal.portal_alarms.slapos_subscription_request_process_planned.setEnabled(True)
self.portal.portal_alarms.slapos_subscription_request_process_confirmed.setEnabled(True)
self.portal.portal_alarms.slapos_subscription_request_process_started.setEnabled(True)
DefaultScenarioMixin.afterSetUp(self)
self.portal.accounting_module.\
template_pre_payment_subscription_sale_invoice_transaction.\
updateLocalRolesOnSecurityGroups()
self.portal.accounting_module.\
slapos_pre_payment_template.\
updateLocalRolesOnSecurityGroups()
self.subscription_project = self.createProject()
# One user to create compute_nodes to deploy the subscription
self.createAdminUser(self.subscription_project)
self.cleanUpNotificationMessage()
self.portal.portal_catalog.searchAndActivate(
portal_type='Active Process',
method_id='ActiveProcess_deleteSelf')
self.tic()
self.createNotificationMessage("subscription_request-confirmation-with-password")
self.createNotificationMessage("subscription_request-confirmation-without-password",
text_content='${name} ${login_name}')
self.createNotificationMessage("subscription_request-instance-is-ready",
text_content='${name} ${subscription_title} ${instance_tree_relative_url}')
self.createNotificationMessage("subscription_request-payment-is-ready",
text_content='${name} ${subscription_title} ${payment_relative_relative_url}')
self.createNotificationMessage("subscription_request-confirmation-with-password", language="zh",
text_content='CHINESE! ${name} ${login_name} ${login_password}')
self.createNotificationMessage("subscription_request-confirmation-without-password", language="zh",
text_content='CHINESE! ${name} ${login_name}')
self.createNotificationMessage("subscription_request-instance-is-ready", language="zh",
text_content='CHINESE! ${name} ${subscription_title} ${instance_tree_relative_url}')
self.createNotificationMessage("subscription_request-payment-is-ready", language="zh",
text_content='CHINESE! ${name} ${subscription_title} ${payment_relative_relative_url}')
self.cleanUpSubscriptionRequest()
self.tic()
self.login()
self.createSubscriptionCondition()
# some preparation
self.logout()
self.web_site = self.portal.web_site_module.hostingjs
# The PaymentTransaction_getVADSUrlDict is required by the implementation
# wechat payments.
def _simulatePaymentTransaction_getVADSUrlDict(self):
script_name = 'PaymentTransaction_getVADSUrlDict'
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,
script_name,
'*args, **kwargs',
'# Script body\n'
"""payment_transaction_url = context.getRelativeUrl()
return dict(vads_url_already_registered="%s/already_registered" % (payment_transaction_url),
vads_url_cancel="%s/cancel" % (payment_transaction_url),
vads_url_error="%s/error" % (payment_transaction_url),
vads_url_referral="%s/referral" % (payment_transaction_url),
vads_url_refused="%s/refused" % (payment_transaction_url),
vads_url_success="%s/success" % (payment_transaction_url),
vads_url_return="%s/return" % (payment_transaction_url),
)""")
def _dropPaymentTransaction_getVADSUrlDict(self):
script_name = 'PaymentTransaction_getVADSUrlDict'
if script_name in self.portal.portal_skins.custom.objectIds():
self.portal.portal_skins.custom.manage_delObjects(script_name)
def cleanUpSubscriptionRequest(self):
for subscription_request in self.portal.portal_catalog(
portal_type="Subscription Request",
simulation_state=["draft", "planned", "ordered", "confirmed"],
title="Test Subscription Request %"):
if subscription_request.getSimulationState() == "draft":
subscription_request.cancel()
if subscription_request.getSimulationState() == "planned":
subscription_request.order()
if subscription_request.getSimulationState() == "ordered":
subscription_request.confirm()
if subscription_request.getSimulationState() == "confirmed":
subscription_request.start()
if subscription_request.getSimulationState() == "started":
subscription_request.stop()
def cleanUpNotificationMessage(self):
for notification_message in self.portal.portal_catalog(
portal_type="Notification Message",
validation_state=["validated"],
title="TestSubscriptionSkins %"):
if str(notification_message.getVersion("")) == "999":
notification_message.invalidate()
def createNotificationMessage(self, reference,
content_type='text/html', language="en", text_content='${name} ${login_name} ${login_password}'):
notification_message = self.portal.notification_message_module.newContent(
portal_type="Notification Message",
text_content_substitution_mapping_method_id='NotificationMessage_getSubstitutionMappingDictFromArgument',
title='TestSubscriptionSkins Notification Message %s %s' % (language, reference),
text_content=text_content,
content_type=content_type,
reference=reference,
version=999,
language=language
)
notification_message.validate()
return notification_message
def createNormalUser(self, email, name, language, project):
""" Create a Normal user """
normal_user_login = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=email,
validation_state="validated"
)
if normal_user_login is None:
normal_user = self.portal.person_module.template_member.\
Base_createCloneDocument(batch_mode=1)
normal_user.newContent(
portal_type="ERP5 Login",
reference=email).validate()
normal_user.edit(
first_name=name,
reference=email,
default_email_text=email,
)
for assignment in normal_user.contentValues(portal_type="Assignment"):
assignment.open()
normal_user.validate()
else:
normal_user = normal_user_login.getParentValue()
normal_user.newContent(
portal_type='Assignment',
destination_project_value=project,
function='customer'
).open()
self.normal_user = normal_user
self.normal_user.setLanguage(language)
def createChineseSubscriptionCondition(self, slave=False):
sale_trade_condition = self.portal.sale_trade_condition_module.newContent(
portal_type="Sale Trade Condition",
title="TestSubscriptionScenario",
source=self.expected_source,
source_section=self.expected_source_section,
source_payment=self.expected_source_section + "/bank_account",
price_currency="currency_module/CNY",
payment_mode='wechat',
specialise="sale_trade_condition_module/slapos_subscription_trade_condition"
)
sale_trade_condition.newContent(
portal_type="Sale Supply Line",
base_price=self.expected_zh_individual_price_without_tax,
resource='service_module/slapos_instance_subscription',
)
sale_trade_condition.newContent(
portal_type="Sale Supply Line",
base_price=self.expected_zh_reservation_fee_without_tax,
resource='service_module/slapos_reservation_fee_2',
)
sale_trade_condition.validate()
subscription_condition = self.portal.subscription_condition_module.newContent(
portal_type="Subscription Condition",
title="TestSubscriptionChineseScenario",
short_tile="Test Your Chinese Scenario",
description="This is a Chinese test",
url_string=self.generateNewSoftwareReleaseUrl(),
root_slave=slave,
default_source_reference="default",
reference="rapidvm%s_zh" % self.new_id,
# Aggregate and Follow up to web pages for product description and
# Terms of service
sla_xml='<?xml version="1.0" encoding="utf-8"?>\n<instance>\n</instance>',
text_content='<?xml version="1.0" encoding="utf-8"?>\n<instance>\n</instance>',
user_input={},
specialise_value=sale_trade_condition,
follow_up_value=self.subscription_project
)
subscription_condition.validate()
self.tic()
return subscription_condition
def createSubscriptionCondition(self, slave=False):
url_string = self.generateNewSoftwareReleaseUrl()
# Create Software Product/Release
software_product = self.portal.software_product_module.newContent(
portal_type="Software Product",
title="TestSoftwareProduct",
follow_up_value=self.subscription_project,
use='trade/sale',
quantity_unit='unit/piece',
product_line='cloud/subscription',
base_contribution_list=[
'base_amount/invoicing/discounted',
'base_amount/invoicing/taxable'
]
)
software_product.validate()
software_release = self.portal.software_release_module.newContent(
portal_type="Software Release",
title="TestSoftwareRelease",
url_string=url_string,
aggregate_value=software_product,
follow_up_value=self.subscription_project
)
software_release.share()
sale_trade_condition = self.portal.sale_trade_condition_module.newContent(
portal_type="Sale Trade Condition",
title="TestSubscriptionScenario",
source=self.expected_source,
source_section=self.expected_source_section,
source_payment=self.expected_source_section + "/bank_account",
price_currency="currency_module/EUR",
payment_mode='payzen',
specialise="sale_trade_condition_module/slapos_subscription_trade_condition",
source_project_value=self.subscription_project
)
# XXX TODO clarify the with / without tax
sale_trade_condition.newContent(
portal_type="Sale Supply Line",
base_price=self.expected_individual_price_without_tax,
resource_value=software_product,
)
sale_trade_condition.newContent(
portal_type="Sale Supply Line",
base_price=self.expected_reservation_fee_without_tax,
resource='service_module/slapos_reservation_fee_2',
)
sale_trade_condition.validate()
self.subscription_condition = self.portal.subscription_condition_module.newContent(
portal_type="Subscription Condition",
title="TestSubscriptionScenario",
short_tile="Test Your Scenario",
description="This is a test",
url_string=url_string,
root_slave=slave,
default_source_reference="default",
reference="rapidvm%s" % self.new_id,
# Aggregate and Follow up to web pages for product description and
# Terms of service
sla_xml='<?xml version="1.0" encoding="utf-8"?>\n<instance>\n</instance>',
text_content='<?xml version="1.0" encoding="utf-8"?>\n<instance>\n</instance>',
user_input={},
specialise_value=sale_trade_condition,
follow_up_value=self.subscription_project
)
self.subscription_condition.validate()
self.tic()
def getSubscriptionRequest(self, email, subscription_condition):
subscription_request_list = subscription_condition.getSpecialiseRelatedValueList(
portal_type='Subscription Request')
self.assertEqual(len(subscription_request_list), 1)
return subscription_request_list[0]
def getSubscriptionRequestList(self, email, subscription_condition):
subscription_request_list = subscription_condition.getSpecialiseRelatedValueList(
portal_type='Subscription Request')
return subscription_request_list
def checkSubscriptionRequest(self, subscription_request, email, subscription_condition):
self.assertNotEqual(subscription_request, None)
self.assertEqual(subscription_request.getDefaultEmailText(), email)
#self.assertEqual(subscription_request.getUrlString(), subscription_condition.getUrlString())
#self.assertEqual(subscription_request.getRootSlave(), subscription_condition.getRootSlave())
#self.assertEqual(subscription_request.getTextContent(),
# '<?xml version="1.0" encoding="utf-8"?>\n<instance>\n</instance>')
#self.assertEqual(trial_request.getSlaXml(), '<?xml version="1.0" encoding="utf-8"?>\n<instance>\n</instance>')
#self.assertEqual(subscription_request.getSourceReference(), "default")
def checkDraftSubscriptionRequest(self, subscription_request, email, subscription_condition,
amount=1):
self.checkSubscriptionRequest(subscription_request, email, subscription_condition)
# XXX This might be diferent
self.assertEqual(subscription_request.getQuantity(), amount)
self.assertEqual(subscription_request.getAggregate(), None)
invoice = subscription_request.getCausalityValue(portal_type="Sale Invoice Transaction")
self.assertNotEqual(invoice, None)
payment = invoice.getCausalityRelatedValue(portal_type="Payment Transaction")
self.assertNotEqual(payment, None)
def checkPlannedSubscriptionRequest(self, subscription_request, email,
subscription_condition):
self.checkSubscriptionRequest(subscription_request, email,
subscription_condition)
self.assertEqual(subscription_request.getSimulationState(), "planned")
def checkOrderedSubscriptionRequest(self, subscription_request, email,
subscription_condition,
notification_message="subscription_request-confirmation-with-password"):
self.checkSubscriptionRequest(subscription_request, email,
subscription_condition)
self.assertEqual(subscription_request.getSimulationState(), "ordered")
self.checkBootstrapUser(subscription_request)
self.checkEmailNotification(subscription_request, notification_message)
def checkConfirmedSubscriptionRequest(self, subscription_request, email,
subscription_condition,
notification_message="subscription_request-payment-is-ready"):
self.checkSubscriptionRequest(subscription_request, email,
subscription_condition)
invoice = subscription_request.SubscriptionRequest_verifyPaymentBalanceIsReady()
self.assertNotEqual(invoice, None)
self.assertEqual(invoice.getSimulationState(), 'stopped')
# Assert instance is allocated and without error
self.assertEqual(True,
subscription_request.SubscriptionRequest_verifyInstanceIsAllocated(verbose=True))
self.assertEqual(subscription_request.getSimulationState(), "confirmed",
"%s != confirmed (%s)" % (subscription_request.getSimulationState(),
subscription_request.SubscriptionRequest_processOrdered()))
self.checkEmailPaymentNotification(subscription_request, notification_message)
def checkStartedSubscriptionRequest(self, subscription_request, email,
subscription_condition,
notification_message="subscription_request-instance-is-ready"):
self.checkSubscriptionRequest(subscription_request, email,
subscription_condition)
self.assertEqual(subscription_request.getSimulationState(), "started")
self.checkEmailInstanceNotification(subscription_request, notification_message)
def checkStoppedSubscriptionRequest(self, subscription_request, email,
subscription_condition):
self.checkSubscriptionRequest(subscription_request, email,
subscription_condition)
self.assertEqual(subscription_request.getSimulationState(), "stopped")
def _getRelatedPaymentValue(self, subscription_request):
invoice = subscription_request.getCausalityValue(portal_type="Sale Invoice Transaction")
return invoice.getCausalityRelatedValue(portal_type="Payment Transaction")
def createReversalInvoiceAndCancelPayment(self, subscription_request):
self.login()
person = subscription_request.getDestinationSectionValue()
self.login(person.getUserId())
invoice_list = person.Entity_getOutstandingAmountList()
self.assertEqual(len(invoice_list), 1)
sale_transaction_invoice = invoice_list[0].getObject()
self.logout()
self.login()
sale_transaction_invoice.SaleInvoiceTransaction_createReversalSaleInvoiceTransaction(batch_mode=1)
def checkSubscriptionRequestPayment(self, subscription_request, authAmount):
self.assertEqual(subscription_request.getSourceSection(), None)
self.assertEqual(subscription_request.getSource(), None)
trade_condition = subscription_request.getSpecialiseValue().getSpecialiseValue()
if trade_condition.getSource() is not None:
#expected_source = subscription_request.getSource()
expected_source_section = trade_condition.getSourceSection()
else:
#expected_source = self.expected_source
expected_source_section = self.expected_source_section
# Be accurate when select the payment
payment = self.portal.portal_catalog.getResultValue(
portal_type="Payment Transaction",
destination_section_uid=subscription_request.getDestinationSectionUid(),
source_section_uid=self.portal.restrictedTraverse(expected_source_section).getUid(),
simulation_state="started")
self.assertEqual(payment.getSourceSection(),
expected_source_section)
self.assertEqual(payment.getSourcePayment(),
"%s/bank_account" % expected_source_section)
self.assertEqual(int(round(payment.PaymentTransaction_getTotalPayablePrice(), 2)*100),
-authAmount)
subscription_condition = subscription_request.getSpecialiseValue(portal_type='Subscription Condition')
sale_trade_condition = subscription_condition.getSpecialiseValue(portal_type='Sale Trade Condition')
self.assertEqual(payment.getPriceCurrency(),
sale_trade_condition.getPriceCurrency())
# Check related invoice Data
invoice_list = payment.getCausalityValueList()
self.assertEqual(len(invoice_list), 1)
invoice = invoice_list[0]
delivery_list = invoice.getCausalityValueList()
# Invoices related to Subscription Request don't merge
self.assertEqual(len(delivery_list), 1)
sale_packing_list = delivery_list[0]
instance_tree = subscription_request.getAggregateValue()
subscription_delivery_line_list = self.portal.portal_catalog(
portal_type="Sale Packing List Line",
resource__uid=instance_tree.InstanceTree_getSoftwareProduct().getUid(),
grouping_reference=sale_packing_list.getReference()
)
self.assertEqual(len(subscription_delivery_line_list), 1,
"len(%s) is not 1" % [i.getObject() for i in subscription_delivery_line_list])
# Check more :)
return payment
def checkAndPayFirstMonth(self, subscription_request):
self.login()
person = subscription_request.getDestinationSectionValue()
quantity = subscription_request.getQuantity()
self.login(person.getUserId())
self.usePaymentManually(
self.web_site,
person.getUserId(),
is_email_expected=False,
subscription_request=subscription_request)
self.logout()
self.login()
# 195 is the month payment
# 195*1 is the 1 months to pay upfront to use.
# 25 is the reservation fee deduction.
authAmount = (int(self.expected_individual_price_with_tax*100)*1-int(self.expected_reservation_fee*100))*quantity
payment = self.checkSubscriptionRequestPayment(subscription_request, authAmount)
data_kw = {
"status": "SUCCESS",
"answer": {
"transactions": [
{
"detailedStatus": "AUTHORISED",
"transactionDetails": {
"cardDetails": {
"authorizationResponse": {
"amount": authAmount,
"currency": "EUR",
},
},
},
}
],
},
}
payment.PaymentTransaction_createPayzenEvent().PayzenEvent_processUpdate(data_kw)
def checkAndPaySecondMonth(self, subscription_request):
self.login()
person = subscription_request.getDestinationSectionValue()
quantity = subscription_request.getQuantity()
self.login(person.getUserId())
self.usePaymentManually(
self.web_site,
person.getUserId(),
subscription_request=subscription_request)
self.logout()
self.login()
authAmount = int(self.expected_individual_price_with_tax*100)*quantity
payment = self.checkSubscriptionRequestPayment(subscription_request, authAmount)
data_kw = {
"status": "SUCCESS",
"answer": {
"transactions": [
{
"detailedStatus": "AUTHORISED",
"transactionDetails": {
"cardDetails": {
"authorizationResponse": {
"amount": authAmount,
"currency": "EUR",
},
},
},
}
],
},
}
payment.PaymentTransaction_createPayzenEvent().PayzenEvent_processUpdate(data_kw)
def checkAndPayFirstMonthViaWechat(self, subscription_request):
self.login()
original_mode = self.portal.portal_secure_payments.slapos_wechat_test.getWechatMode()
self._simulatePaymentTransaction_getVADSUrlDict()
try:
person = subscription_request.getDestinationSectionValue()
quantity = subscription_request.getQuantity()
self.portal.portal_secure_payments.slapos_wechat_test.setWechatMode("UNITTEST")
self.login(person.getUserId())
self.usePaymentManually(
self.web_site,
person.getUserId(),
is_email_expected=False,
subscription_request=subscription_request)
self.logout()
self.login()
authAmount = (int(self.expected_zh_individual_price_with_tax*100)*1-int(self.expected_zh_reservation_fee*100))*quantity
payment = self.checkSubscriptionRequestPayment(subscription_request, authAmount)
data_kw = {
'result_code': 'SUCCESS',
'trade_state': 'SUCCESS',
'total_fee': authAmount,
'fee_type': 'CNY',
}
# Wechat_processUpdate will mark payment as payed by stopping it.
payment.PaymentTransaction_createWechatEvent().WechatEvent_processUpdate(data_kw)
finally:
self._dropPaymentTransaction_getVADSUrlDict()
self.portal.portal_secure_payments.slapos_wechat_test.setWechatMode(original_mode)
def checkAndPaySecondMonthViaWechat(self, subscription_request):
self.login()
original_mode = self.portal.portal_secure_payments.slapos_wechat_test.getWechatMode()
self._simulatePaymentTransaction_getVADSUrlDict()
try:
person = subscription_request.getDestinationSectionValue()
quantity = subscription_request.getQuantity()
self.portal.portal_secure_payments.slapos_wechat_test.setWechatMode("UNITTEST")
self.login(person.getUserId())
self.usePaymentManually(
self.web_site,
person.getUserId(),
subscription_request=subscription_request)
self.logout()
self.login()
authAmount = int(self.expected_zh_individual_price_with_tax*100)*quantity
payment = self.checkSubscriptionRequestPayment(subscription_request, authAmount)
data_kw = {
'result_code': 'SUCCESS',
'trade_state': 'SUCCESS',
'total_fee': authAmount,
'fee_type': 'CNY',
}
# Wechat_processUpdate will mark payment as payed by stopping it.
payment.PaymentTransaction_createWechatEvent().WechatEvent_processUpdate(data_kw)
finally:
self._dropPaymentTransaction_getVADSUrlDict()
self.portal.portal_secure_payments.slapos_wechat_test.setWechatMode(original_mode)
def _checkFreeReservationPayment(self, subscription_request):
quantity = subscription_request.getQuantity()
# Check Payment
payment = self._getRelatedPaymentValue(subscription_request)
subscription_condition = subscription_request.getSpecialiseValue(portal_type='Subscription Condition')
sale_trade_condition = subscription_condition.getSpecialiseValue(portal_type='Sale Trade Condition')
self.assertEqual(sale_trade_condition.getPriceCurrency(),
payment.getPriceCurrency())
self.assertEqual(-self.expected_reservation_fee*quantity,
payment.PaymentTransaction_getTotalPayablePrice())
self.assertEqual(payment.getSimulationState(), "stopped")
return payment
def _payPayment(self, subscription_request):
quantity = subscription_request.getQuantity()
# Check Payment
payment = self._getRelatedPaymentValue(subscription_request)
subscription_condition = subscription_request.getSpecialiseValue(portal_type='Subscription Condition')
sale_trade_condition = subscription_condition.getSpecialiseValue(portal_type='Sale Trade Condition')
#sale_supply_line = sale_trade_condition.contentValues(portal_type='Sale Supply Line')[0]
self.assertEqual(sale_trade_condition.getPriceCurrency(),
payment.getPriceCurrency())
self.assertEqual(payment.getSimulationState(), "started")
# Pay with appropriate mode depending of the currency.
if payment.getPriceCurrency() == "currency_module/CNY":
self.assertEqual(-round(self.expected_zh_reservation_fee*quantity, 2),
round(payment.PaymentTransaction_getTotalPayablePrice(), 2))
# Pay 188 CNY per VM
data_kw = {
'result_code': 'SUCCESS',
'trade_state': 'SUCCESS',
'total_fee': self.expected_zh_reservation_fee*100*quantity,
'fee_type': 'CNY',
}
# Wechat_processUpdate will mark payment as payed by stopping it.
payment.PaymentTransaction_createWechatEvent().WechatEvent_processUpdate(data_kw)
else:
self.assertEqual(-round(self.expected_reservation_fee*quantity, 2),
round(payment.PaymentTransaction_getTotalPayablePrice(), 2))
# Pay 25 euros per VM
data_kw = {
"status": "SUCCESS",
"answer": {
"transactions": [
{
"detailedStatus": "AUTHORISED",
"transactionDetails": {
"cardDetails": {
"authorizationResponse": {
"amount": self.expected_reservation_fee*100*quantity,
"currency": "EUR",
},
},
},
}
],
},
}
# Payzen_processUpdate will mark payment as payed by stopping it.
payment.PaymentTransaction_createPayzenEvent().PayzenEvent_processUpdate(data_kw)
return payment
def checkAndPaySubscriptionPayment(self, subscription_request_list):
for subscription_request in subscription_request_list:
quantity = subscription_request.getQuantity()
invoice = subscription_request.getCausalityValue(
portal_type="Sale Invoice Transaction")
self.assertEqual(invoice.getSimulationState(), "confirmed")
self.assertEqual(invoice.getCausalityState(), "building")
self.assertEqual(subscription_request.getSourceSection(), None)
self.assertEqual(subscription_request.getSource(), None)
self.assertEqual(subscription_request.getSpecialiseValue().getSourceSection(), None)
self.assertEqual(subscription_request.getSpecialiseValue().getSource(), None)
trade_condition = subscription_request.getSpecialiseValue().getSpecialiseValue()
if trade_condition.getSource() is not None:
expected_source = trade_condition.getSource()
expected_source_section = trade_condition.getSourceSection()
else:
expected_source = self.expected_source
expected_source_section = self.expected_source_section
self.assertEqual(invoice.getSource(), expected_source)
self.assertEqual(invoice.getSourceSection(), expected_source_section)
# Pay Invoice if it is not Free
if not self.expected_free_reservation:
payment = self._payPayment(subscription_request)
else:
payment = self._checkFreeReservationPayment(subscription_request)
# Check Payment
self.assertEqual(payment.getSourceSection(), expected_source_section)
self.assertEqual(payment.getSourcePayment(),
"%s/bank_account" % expected_source_section)
self.tic()
self.assertEqual(payment.getSimulationState(), "stopped")
# stabilise aggregated invoices and expand them
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# update invoices with their tax & discount
self.stepCallSlaposTriggerBuildAlarm()
self.tic()
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# update invoices with their tax & discount transaction lines
self.stepCallSlaposTriggerBuildAlarm()
self.tic()
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# stop the invoices and solve them again
self.stepCallSlaposStopConfirmedAggregatedSaleInvoiceTransactionAlarm()
self.tic()
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
for subscription_request in subscription_request_list:
invoice = subscription_request.getCausalityValue(
portal_type="Sale Invoice Transaction")
subscription_condition = subscription_request.getSpecialiseValue(portal_type='Subscription Condition')
sale_trade_condition = subscription_condition.getSpecialiseValue(portal_type='Sale Trade Condition')
#sale_supply_line = sale_trade_condition.contentValues(portal_type='Sale Supply Line')[0]
if sale_trade_condition.getPriceCurrency() == "currency_module/CNY":
expected_reservation_fee_without_tax = self.expected_zh_reservation_fee_without_tax
expected_reservation_quantity_tax = self.expected_zh_reservation_quantity_tax
expected_reservation_tax = self.expected_zh_reservation_tax
expected_reservation_fee = self.expected_zh_reservation_fee
else:
expected_reservation_fee_without_tax = self.expected_reservation_fee_without_tax
expected_reservation_quantity_tax = self.expected_reservation_quantity_tax
expected_reservation_tax = self.expected_reservation_tax
expected_reservation_fee = self.expected_reservation_fee
self.assertEqual(invoice.getSimulationState(), "stopped", invoice.getRelativeUrl())
self.assertEqual(invoice.getCausalityState(), "solved")
self.assertEqual(invoice.getPriceCurrency(),
sale_trade_condition.getPriceCurrency())
for line in invoice.objectValues():
if line.getResource() == "service_module/slapos_reservation_fee_2":
self.assertEqual(line.getTotalQuantity(), quantity)
if self.expected_free_reservation:
self.assertEqual(round(line.getTotalPrice(), 2), 0.0)
else:
self.assertEqual(round(line.getTotalPrice(), 2),
round(expected_reservation_fee_without_tax*quantity, 2))
if line.getResource() == "service_module/slapos_tax":
self.assertEqual(round(line.getTotalQuantity(), 2),
round(expected_reservation_quantity_tax*quantity, 2))
self.assertEqual(round(line.getTotalPrice(), 2),
round(expected_reservation_tax*quantity, 2))
self.assertEqual(round(invoice.getTotalPrice(), 2),
round(expected_reservation_fee*quantity, 2))
def checkSecondMonthAggregatedSalePackingList(self, subscription_request, sale_packing_list):
sale_packing_list_line = [ i for i in sale_packing_list.objectValues()
if i.getResource() == "service_module/slapos_instance_subscription"][0]
quantity = subscription_request.getQuantity()
subscription_condition = subscription_request.getSpecialiseValue(portal_type='Subscription Condition')
sale_trade_condition = subscription_condition.getSpecialiseValue(portal_type='Sale Trade Condition')
# The values are without tax
if sale_trade_condition.getPriceCurrency() == "currency_module/CNY":
expected_individual_price_without_tax = self.expected_zh_individual_price_without_tax
else:
expected_individual_price_without_tax = self.expected_individual_price_without_tax
self.assertEqual(sale_packing_list_line.getQuantity(), 1*quantity)
self.assertEqual(round(sale_packing_list_line.getPrice(), 2),
round(expected_individual_price_without_tax, 2))
self.assertEqual(round(sale_packing_list_line.getTotalPrice(), 2),
round(1*expected_individual_price_without_tax*quantity, 2))
self.assertEqual(sale_packing_list.getCausality(),
subscription_request.getRelativeUrl())
self.assertEqual(sale_packing_list.getCausality(),
subscription_request.getRelativeUrl())
self.assertEqual(sale_packing_list.getPriceCurrency(),
sale_trade_condition.getPriceCurrency())
def _checkSecondMonthSimulation(self, subscription_request_list,
default_email_text, subscription_server):
# Needed?
self.simulateSlapgridCP(subscription_server)
self.tic()
self.stepCallSlaposUpdateOpenSaleOrderPeriodAlarm()
self.tic()
for subscription_request in subscription_request_list:
self.checkAllocationOnRelatedInstance(subscription_request)
# Needed?
self.simulateSlapgridCP(subscription_server)
self.tic()
# generate simulation for open order
self.stepCallUpdateOpenOrderSimulationAlarm(full=1)
self.tic()
# build subscription packing list
self.stepCallSlaposTriggerBuildAlarm()
self.tic()
# stabilise build deliveries and expand them
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# build aggregated packing list
self.stepCallSlaposTriggerAggregatedDeliveryOrderBuilderAlarm()
self.tic()
# stabilise aggregated deliveries and expand them
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# check if Packing list is generated with the right trade condition
preference_tool = self.portal.portal_preferences
aggregate_subscription_condition = \
preference_tool.getPreferredAggregatedSubscriptionSaleTradeCondition()
trade_condition = preference_tool.getPreferredAggregatedSaleTradeCondition()
for subscription_request in subscription_request_list:
sale_packing_list_list = self.getAggregatedSalePackingList(
subscription_request, aggregate_subscription_condition)
if not len(sale_packing_list_list):
diverged_sale_packing_list_list = self.getDivergedAggregatedSalePackingList(
subscription_request, aggregate_subscription_condition)
self.assertEqual(0, len(diverged_sale_packing_list_list))
self.assertEqual(1, len(sale_packing_list_list))
self.checkSecondMonthAggregatedSalePackingList(subscription_request, sale_packing_list_list[0])
expected_sale_packing_list_amount = (len(subscription_request_list) * 2)+\
self.non_subscription_related_instance_amount
self.assertEqual(expected_sale_packing_list_amount,
len(self.getSubscriptionSalePackingList(subscription_request)))
self.assertEqual(0, len(self.getAggregatedSalePackingList(
subscription_request, trade_condition)))
# Call this alarm shouldn't affect the delivery
self.stepCallSlaposStartConfirmedAggregatedSalePackingListAlarm(
accounting_date=DateTime('2222/01/01'))
self.tic()
for subscription_request in subscription_request_list:
self.assertEqual(1, len(self.getAggregatedSalePackingList(
subscription_request, aggregate_subscription_condition)))
# Call this alarm shouldn't affect the delivery
self.stepCallSlaposStartConfirmedAggregatedSubscriptionSalePackingListAlarm()
self.tic()
for subscription_request in subscription_request_list:
self.assertEqual(0, len(self.getAggregatedSalePackingList(
subscription_request, trade_condition)))
# stabilise aggregated deliveries and expand them
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# deliver aggregated deliveries
self.stepCallSlaposDeliverStartedAggregatedSalePackingListAlarm()
self.tic()
# stabilise aggregated deliveries and expand them
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# build aggregated invoices
self.stepCallSlaposTriggerBuildAlarm()
self.tic()
# stabilise aggregated invoices and expand them
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# update invoices with their tax & discount
self.stepCallSlaposTriggerBuildAlarm()
self.tic()
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# update invoices with their tax & discount transaction lines
self.stepCallSlaposTriggerBuildAlarm()
self.tic()
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# stop the invoices and solve them again
self.stepCallSlaposStopConfirmedAggregatedSaleInvoiceTransactionAlarm()
self.tic()
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# trigger the CRM interaction
self.stepCallSlaposCrmCreateRegularisationRequestAlarm()
self.tic()
# Test if balance is bad now
subscriber = subscription_request.getDestinationSectionValue()
self.assertEqual(subscriber.Entity_statOutstandingAmount(at_date=DateTime()),
0.0)
trade_condition = subscription_request.getSpecialiseValue().getSpecialiseValue()
if trade_condition.getPriceCurrency() == "currency_module/CNY":
expected_individual_price_with_tax = self.expected_zh_individual_price_with_tax
else:
expected_individual_price_with_tax = self.expected_individual_price_with_tax
expected_amount = round(expected_individual_price_with_tax*sum([i.getQuantity(0)
for i in subscription_request_list]),2)
self.assertEqual(round(subscriber.Entity_statOutstandingAmount(), 2), expected_amount)
# Invoice to Pay
self.assertEqual(
round(subscriber.Entity_statSlapOSOutstandingAmount(at_date=DateTime()+20), 2),
expected_amount)
# Pay this new invoice
for subscription_request in subscription_request_list:
trade_condition = subscription_request.getSpecialiseValue().getSpecialiseValue()
if trade_condition.getPriceCurrency() == "currency_module/CNY":
self.checkAndPaySecondMonthViaWechat(subscription_request)
else:
self.checkAndPaySecondMonth(subscription_request)
self.tic()
# Here the invoice was payed before the date, so value is negative.
self.assertEqual(round(subscriber.Entity_statOutstandingAmount(at_date=DateTime()), 2),
0.0)
self.assertEqual(round(subscriber.Entity_statOutstandingAmount(), 2), 0.0)
# All payed
self.assertEqual(
round(subscriber.Entity_statSlapOSOutstandingAmount(at_date=DateTime()+20), 2),
0.0)
def checkBootstrapUser(self, subscription_request):
person = subscription_request.getDestinationSectionValue(portal_type="Person")
self.assertEqual(person.getDefaultEmailText(),
subscription_request.getDefaultEmailText())
self.assertEqual(person.getValidationState(), "validated")
login_list = [x for x in person.objectValues(portal_type='ERP5 Login') \
if x.getValidationState() == 'validated']
self.assertEqual(len(login_list), 1)
self.assertEqual(login_list[0].getReference(), person.getDefaultEmailText())
def checkEmailNotification(self, subscription_request,
notification_message="subscription_request-confirmation-with-password"):
expected_amount = 1
if self.normal_user is not None:
# If user already exists we do not expect to send an email
expected_amount = 0
mail_message_list = [i for i in subscription_request.getFollowUpRelatedValueList(
portal_type="Mail Message") if notification_message in i.getTitle()]
self.assertEqual(len(mail_message_list), expected_amount)
if not expected_amount:
return
mail_message = mail_message_list[0]
self.assertEqual(
"TestSubscriptionSkins Notification Message %s %s" % (
subscription_request.getLanguage(), notification_message),
mail_message.getTitle())
self.assertTrue(subscription_request.getDefaultEmailText() in \
mail_message.getTextContent())
self.assertTrue(subscription_request.getDestinationSectionTitle() in \
mail_message.getTextContent())
def checkEmailPaymentNotification(self, subscription_request,
notification_message="subscription_request-payment-is-ready"):
mail_message_list = [i for i in subscription_request.getFollowUpRelatedValueList(
portal_type="Mail Message") if notification_message in i.getTitle()]
self.assertEqual(len(mail_message_list), 1)
mail_message = mail_message_list[0]
self.assertEqual(
"TestSubscriptionSkins Notification Message %s %s" % (
subscription_request.getLanguage(), notification_message),
mail_message.getTitle())
invoice = subscription_request.SubscriptionRequest_verifyPaymentBalanceIsReady()
self.assertEqual(invoice.getSimulationState(), 'stopped')
self.assertTrue(invoice.getRelativeUrl() in \
mail_message.getTextContent())
self.assertTrue(subscription_request.getDestinationSectionTitle() in \
mail_message.getTextContent())
def checkEmailInstanceNotification(self, subscription_request,
notification_message="subscription_request-instance-is-ready"):
mail_message_list = [i for i in subscription_request.getFollowUpRelatedValueList(
portal_type="Mail Message") if notification_message in i.getTitle()]
self.assertEqual(len(mail_message_list), 1)
mail_message = mail_message_list[0]
self.assertEqual(
"TestSubscriptionSkins Notification Message %s %s" % (
subscription_request.getLanguage(), notification_message),
mail_message.getTitle())
instance_tree = subscription_request.getAggregateValue()
self.assertEqual(instance_tree.getSlapState(), 'start_requested')
self.assertTrue(instance_tree.getRelativeUrl() in \
mail_message.getTextContent())
self.assertTrue(subscription_request.getDestinationSectionTitle() in \
mail_message.getTextContent())
def checkRelatedInstance(self, subscription_request):
instance = self._checkRelatedInstance(subscription_request)
self.assertEqual(instance.getAggregate(), None)
def _checkRelatedInstance(self, subscription_request):
instance_tree = subscription_request.getAggregateValue()
self.assertNotEqual(instance_tree, None)
subscription_condition = subscription_request.getSpecialiseValue(portal_type='Subscription Condition')
self.assertEqual(subscription_condition.getUrlString(),
instance_tree.getUrlString())
self.assertEqual(subscription_condition.getRootSlave(),
instance_tree.getRootSlave())
self.assertEqual(instance_tree.getTextContent(),
'<?xml version="1.0" encoding="utf-8"?>\n<instance>\n</instance>')
#self.assertEqual(trial_request.getSlaXml(), '<?xml version="1.0" encoding="utf-8"?>\n<instance>\n</instance>')
self.assertEqual(instance_tree.getSourceReference(), "default")
instance_list = instance_tree.getSpecialiseRelatedValueList(
portal_type=["Software Instance", "Slave Instance"])
self.assertEqual(1,len(instance_list))
return instance_list[0]
def checkAllocationOnRelatedInstance(self, subscription_request):
instance = self._checkRelatedInstance(subscription_request)
self.assertNotEqual(instance.getAggregate(), None)
def checkAggregatedSalePackingList(self, subscription_request, sale_packing_list):
subscription_condition = subscription_request.getSpecialiseValue(portal_type='Subscription Condition')
sale_trade_condition = subscription_condition.getSpecialiseValue(portal_type='Sale Trade Condition')
#sale_supply_line = sale_trade_condition.contentValues(portal_type='Sale Supply Line')[0]
self.assertEqual(len(sale_packing_list.objectValues()), 2)
sale_packing_list_line = [ i for i in sale_packing_list.objectValues()
if i.getResourceValue().getPortalType() == "Software Product"][0]
quantity = subscription_request.getQuantity()
if sale_trade_condition.getPriceCurrency() == "currency_module/CNY":
expected_individual_price_without_tax = self.expected_zh_individual_price_without_tax
expected_reservation_fee = self.expected_zh_reservation_fee_without_tax
else:
expected_individual_price_without_tax = self.expected_individual_price_without_tax
expected_reservation_fee = self.expected_reservation_fee_without_tax
# The values are without tax
self.assertEqual(sale_packing_list_line.getQuantity(), 1*quantity)
self.assertEqual(round(sale_packing_list_line.getPrice(), 2),
round(expected_individual_price_without_tax, 2))
self.assertEqual(round(sale_packing_list_line.getTotalPrice(), 2),
round(1*expected_individual_price_without_tax*quantity, 2))
self.assertEqual(sale_packing_list.getCausality(),
subscription_request.getRelativeUrl())
sale_packing_list_line = [ i for i in sale_packing_list.objectValues()
if i.getResource() == "service_module/slapos_reservation_refund"][0]
quantity = subscription_request.getQuantity()
# The values are without tax
self.assertEqual(sale_packing_list_line.getQuantity(), 1)
self.assertEqual(round(sale_packing_list_line.getPrice(), 2),
-round(expected_reservation_fee*quantity, 2))
self.assertEqual(round(sale_packing_list_line.getTotalPrice(), 2),
-round(expected_reservation_fee*quantity, 2))
self.assertEqual(sale_packing_list.getCausality(),
subscription_request.getRelativeUrl())
self.assertEqual(sale_packing_list.getPriceCurrency(),
sale_trade_condition.getPriceCurrency())
def makeCloudInvitationToken(self, max_invoice_delay=0, max_invoice_credit_eur=0.0,
max_invoice_credit_cny=0.0):
self.login()
contract_invitation_token = self.portal.invitation_token_module.newContent(
portal_type="Contract Invitation Token",
maximum_invoice_delay=max_invoice_delay
)
if max_invoice_credit_eur > 0:
contract_invitation_token.newContent(
maximum_invoice_credit=max_invoice_credit_eur,
price_currency="currency_module/EUR")
if max_invoice_credit_cny > 0:
contract_invitation_token.newContent(
maximum_invoice_credit=max_invoice_credit_cny,
price_currency="currency_module/CNY")
contract_invitation_token.validate()
self.logout()
return contract_invitation_token
@changeSkin('Hal')
def _requestSubscription(self, **kw):
if self.cloud_invitation_token is not None:
kw["token"] = self.cloud_invitation_token.getId()
if self.resource_variation_reference is not None:
kw["variation_reference"] = self.resource_variation_reference
return self.web_site.hateoas.SubscriptionRequestModule_requestSubscriptionProxy(**kw)
@changeSkin('Hal')
def _requestSubscriptionViaChineseWebsite(self, **kw):
if self.cloud_invitation_token is not None:
kw["token"] = self.cloud_invitation_token.getId()
if 'target_language' not in kw:
kw["target_language"] = "zh"
if self.resource_variation_reference is not None:
kw["variation_reference"] = self.resource_variation_reference
kw["subscription_reference"] = self.subscription_condition.getReference().replace("_zh", "")
original_mode = self.portal.portal_secure_payments.slapos_wechat_test.getWechatMode()
self._simulatePaymentTransaction_getVADSUrlDict()
try:
self.portal.portal_secure_payments.slapos_wechat_test.setWechatMode("UNITTEST")
self.logout()
self.changeSkin('Hal')
return self.web_site.hateoas.SubscriptionRequestModule_requestSubscriptionProxy(**kw)
finally:
self._dropPaymentTransaction_getVADSUrlDict()
self.portal.portal_secure_payments.slapos_wechat_test.setWechatMode(original_mode)
def getAggregatedSalePackingList(self, subscription_request, specialise):
person = subscription_request.getDestinationSectionValue()
person_uid = person.getUid()
specialise_uid = self.portal.restrictedTraverse(specialise).getUid()
return self.portal.portal_catalog(
portal_type='Sale Packing List',
simulation_state='confirmed',
causality_state='solved',
causality_uid=subscription_request.getUid(),
destination_decision_uid=person_uid,
specialise_uid=specialise_uid)
def getDivergedAggregatedSalePackingList(self, subscription_request, specialise):
person_uid = subscription_request.getDestinationSectionValue().getUid()
specialise_uid = self.portal.restrictedTraverse(specialise).getUid()
return self.portal.portal_catalog(
portal_type='Sale Packing List',
simulation_state='confirmed',
causality_state='diverged',
causality_uid=subscription_request.getUid(),
destination_decision_uid=person_uid,
specialise_uid=specialise_uid)
def getSubscriptionSalePackingList(self, subscription_request):
person_uid = subscription_request.getDestinationSectionValue().getUid()
specialise_uid = self.portal.restrictedTraverse(
"sale_trade_condition_module/slapos_subscription_trade_condition").getUid()
specialise_uid = [specialise_uid] + [
i.uid for i in self.portal.ERP5Site_searchRelatedInheritedSpecialiseList(
specialise_uid=specialise_uid)
]
return self.portal.portal_catalog(
portal_type='Sale Packing List',
simulation_state='delivered',
causality_state='solved',
destination_decision_uid=person_uid,
specialise_uid=specialise_uid)
def createPublicServerForAdminUser(self):
# hooray, now it is time to create compute_nodes
self.login(self.admin_user.getUserId())
# Create a Public Server for admin user, and allow
subscription_server_title = 'Trial Public Server for Admin User %s' % self.new_id
subscription_server_id = self.requestComputeNode(subscription_server_title,
self.subscription_project.getReference())
subscription_server = self.portal.portal_catalog.getResultValue(
portal_type='Compute Node', reference=subscription_server_id)
self.setAccessToMemcached(subscription_server)
self.assertNotEqual(None, subscription_server)
self.setServerOpenPublic(subscription_server)
self.assertEqual('open', subscription_server.getCapacityScope())
# and install some software on them
subscription_server_software = self.subscription_condition.getUrlString()
self.supplySoftware(subscription_server, subscription_server_software)
# format the compute_nodes
self.formatComputeNode(subscription_server)
self.tic()
self.logout()
return subscription_server
def requestAndCheckInstanceTree(self, amount, name,
default_email_text):
self.logout()
user_input_dict = {
"name": name,
"amount" : amount}
self._requestSubscription(
subscription_reference=self.subscription_condition.getReference(),
user_input_dict=user_input_dict,
email=default_email_text,
confirmation_required=False)
self.login()
# I'm not sure if this is realistic
self.tic()
subscription_request = self.getSubscriptionRequest(
default_email_text, self.subscription_condition)
sale_trade_condition = self.subscription_condition.getSpecialiseValue(
portal_type="Sale Trade Condition",
)
self.assertEqual(self.expected_price_currency,
sale_trade_condition.getPriceCurrency())
self.assertEqual(self.expected_notification_language,
subscription_request.getLanguage())
self.checkDraftSubscriptionRequest(subscription_request,
default_email_text, self.subscription_condition,
amount=amount)
# Check Payment and pay it.
self.checkAndPaySubscriptionPayment([subscription_request])
self.tic()
# Call alarm to check payment and invoice and move foward to planned.
self.stepCallSlaposSubscriptionRequestProcessDraftAlarm()
self.tic()
self.checkPlannedSubscriptionRequest(subscription_request,
default_email_text, self.subscription_condition)
# Call alarm to mark subscription request as ordered, bootstrap the user
# and check if email is sent, once done move to ordered state.
self.stepCallSlaposSubscriptionRequestProcessPlannedAlarm()
self.tic()
self.checkOrderedSubscriptionRequest(subscription_request,
default_email_text, self.subscription_condition)
# Call alarm to make the request of the instance?
self.stepCallSlaposSubscriptionRequestProcessOrderedAlarm()
self.tic()
# The alarms might be called multiple times for move each step
self.stepCallSlaposSubscriptionRequestProcessOrderedAlarm()
self.tic()
sale_packing_list_list = self.portal.portal_catalog(
causality_uid = subscription_request.getUid(),
title="Reservation Deduction",
portal_type="Sale Packing List"
)
self.assertEqual(len(sale_packing_list_list), 1)
sale_packing_list = sale_packing_list_list[0]
subscription_condition = subscription_request.getSpecialiseValue(portal_type='Subscription Condition')
sale_trade_condition = subscription_condition.getSpecialiseValue(portal_type='Sale Trade Condition')
#sale_supply_line = sale_trade_condition.contentValues(portal_type='Sale Supply Line')[0]
self.assertEqual(sale_packing_list.getPriceCurrency(),
sale_trade_condition.getPriceCurrency())
self.assertEqual(sale_packing_list.getSpecialise(),
"sale_trade_condition_module/slapos_reservation_refund_trade_condition")
if sale_trade_condition.getPriceCurrency() == "currency_module/CNY":
self.assertEqual(round(sale_packing_list.getTotalPrice(), 2),
-round(self.expected_zh_reservation_fee_without_tax*amount, 2))
else:
self.assertEqual(round(sale_packing_list.getTotalPrice(), 2),
-round(self.expected_reservation_fee_without_tax*amount, 2))
return subscription_request
def _checkSubscriptionDeploymentAndSimulation(self, subscription_request_list,
default_email_text, subscription_server):
for subscription_request in subscription_request_list:
# Check if instance was requested
self.checkRelatedInstance(subscription_request)
self.simulateSlapgridCP(subscription_server)
self.tic()
self.stepCallSlaposAllocateInstanceAlarm()
self.tic()
# now instantiate it on compute_node and set some nice connection dict
self.simulateSlapgridCP(subscription_server)
self.tic()
for subscription_request in subscription_request_list:
# Re-check, as instance shouldn't be allocated until
# the confirmation of the new Payment.
self.checkAllocationOnRelatedInstance(subscription_request)
# generate simulation for open order
self.stepCallUpdateOpenOrderSimulationAlarm()
self.tic()
# build subscription packing list
self.stepCallSlaposTriggerBuildAlarm()
self.tic()
# stabilise build deliveries and expand them
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# build aggregated packing list
self.stepCallSlaposTriggerAggregatedDeliveryOrderBuilderAlarm()
self.tic()
# stabilise aggregated deliveries and expand them
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# check if Packing list is generated with the right trade condition
preference_tool = self.portal.portal_preferences
aggregate_subscription_condition = \
preference_tool.getPreferredAggregatedSubscriptionSaleTradeCondition()
trade_condition = preference_tool.getPreferredAggregatedSaleTradeCondition()
for subscription_request in subscription_request_list:
instance_tree = subscription_request.getAggregateValue()
self.assertEqual(instance_tree.getCausalityState(), "solved")
sale_packing_list_list = self.getAggregatedSalePackingList(
subscription_request, aggregate_subscription_condition)
self.assertEqual(1, len(sale_packing_list_list))
self.checkAggregatedSalePackingList(subscription_request, sale_packing_list_list[0])
expected_sale_packing_list_amount = len(subscription_request_list) +\
self.non_subscription_related_instance_amount
self.assertEqual(expected_sale_packing_list_amount,
len(self.getSubscriptionSalePackingList(subscription_request)))
self.assertEqual(0, len(self.getAggregatedSalePackingList(
subscription_request, trade_condition)))
# Call this alarm shouldn't affect the delivery
self.stepCallSlaposStartConfirmedAggregatedSalePackingListAlarm(
accounting_date=DateTime('2222/01/01'))
self.tic()
for subscription_request in subscription_request_list:
self.assertEqual(1, len(self.getAggregatedSalePackingList(
subscription_request, aggregate_subscription_condition)))
# Call this alarm shouldn't affect the delivery
self.stepCallSlaposStartConfirmedAggregatedSubscriptionSalePackingListAlarm()
self.tic()
for subscription_request in subscription_request_list:
self.assertEqual(0, len(self.getAggregatedSalePackingList(
subscription_request, trade_condition)))
# stabilise aggregated deliveries and expand them
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# deliver aggregated deliveries
self.stepCallSlaposDeliverStartedAggregatedSalePackingListAlarm()
self.tic()
# stabilise aggregated deliveries and expand them
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# build aggregated invoices
self.stepCallSlaposTriggerBuildAlarm()
self.tic()
# stabilise aggregated invoices and expand them
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# update invoices with their tax & discount
self.stepCallSlaposTriggerBuildAlarm()
self.tic()
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# update invoices with their tax & discount transaction lines
self.stepCallSlaposTriggerBuildAlarm()
self.tic()
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# stop the invoices and solve them again
self.stepCallSlaposStopConfirmedAggregatedSaleInvoiceTransactionAlarm()
self.tic()
self.stepCallSlaposManageBuildingCalculatingDeliveryAlarm()
self.tic()
# trigger the CRM interaction
self.stepCallSlaposCrmCreateRegularisationRequestAlarm()
self.tic()
# After the payment re-call the Alarm in order to confirm the subscription
# Request.
self.stepCallSlaposSubscriptionRequestProcessOrderedAlarm()
self.tic()
# The alarms might be called multiple times for move each step
self.stepCallSlaposSubscriptionRequestProcessOrderedAlarm()
self.tic()
for subscription_request in subscription_request_list:
# Instances should be allocated
self.checkAllocationOnRelatedInstance(subscription_request)
if not self.expected_free_reservation:
# In a scenario where invitation token is used, we expect
# that this script outputs True as user is below the maximum limit.
expected_test_payment_balance = False
expected_slap_state_after_subscription_is_confirmed = 'stop_requested'
else:
expected_test_payment_balance = True
expected_slap_state_after_subscription_is_confirmed = 'start_requested'
# Check if instance is on confirmed state
for subscription_request in subscription_request_list:
self.checkConfirmedSubscriptionRequest(subscription_request,
default_email_text,
subscription_request.getSpecialiseValue())
self.assertEqual(expected_test_payment_balance,
subscription_request.SubscriptionRequest_testPaymentBalance())
self.assertEqual('start_requested',
subscription_request.getAggregateValue().getSlapState())
# The alarms might be called multiple times for move each step
self.stepCallSlaposSubscriptionRequestProcessConfirmedAlarm()
self.tic()
for subscription_request in subscription_request_list:
self.assertEqual(expected_test_payment_balance,
subscription_request.SubscriptionRequest_testPaymentBalance())
self.assertEqual(expected_slap_state_after_subscription_is_confirmed,
subscription_request.getAggregateValue().getSlapState())
def checkSubscriptionDeploymentAndSimulationWithReversalTransaction(self, default_email_text, subscription_server):
subscription_request_list = self.getSubscriptionRequestList(
default_email_text, self.subscription_condition)
self._checkSubscriptionDeploymentAndSimulation(
subscription_request_list, default_email_text, subscription_server)
# Make payments and reinvoke alarm
for subscription_request in subscription_request_list:
self.createReversalInvoiceAndCancelPayment(subscription_request)
self.tic()
self.stepCallSlaposSubscriptionRequestProcessConfirmedAlarm()
self.tic()
for subscription_request in subscription_request_list:
self.assertTrue(
subscription_request.SubscriptionRequest_testPaymentBalance())
self.assertEqual('start_requested',
subscription_request.getAggregateValue().getSlapState())
# On the second loop that email is send and state is moved to started
self.stepCallSlaposSubscriptionRequestProcessConfirmedAlarm()
self.tic()
for subscription_request in subscription_request_list:
self.assertTrue(
subscription_request.SubscriptionRequest_testPaymentBalance())
self.assertEqual('start_requested',
subscription_request.getAggregateValue().getSlapState())
self.checkStartedSubscriptionRequest(subscription_request,
default_email_text, self.subscription_condition)
def checkSubscriptionDeploymentAndSimulation(self, default_email_text, subscription_server):
subscription_request_list = self.getSubscriptionRequestList(
default_email_text, self.subscription_condition)
self._checkSubscriptionDeploymentAndSimulation(
subscription_request_list, default_email_text, subscription_server)
if not self.expected_free_reservation:
for subscription_request in subscription_request_list:
subscription_condition = subscription_request.getSpecialiseValue(portal_type='Subscription Condition')
sale_trade_condition = subscription_condition.getSpecialiseValue(portal_type='Sale Trade Condition')
if sale_trade_condition.getPriceCurrency() == "currency_module/CNY":
self.checkAndPayFirstMonthViaWechat(subscription_request)
else:
self.checkAndPayFirstMonth(subscription_request)
self.tic()
self.stepCallSlaposSubscriptionRequestProcessConfirmedAlarm()
self.tic()
for subscription_request in subscription_request_list:
self.assertTrue(
subscription_request.SubscriptionRequest_testPaymentBalance())
self.assertEqual('start_requested',
subscription_request.getAggregateValue().getSlapState())
# On the second loop that email is send and state is moved to started
self.stepCallSlaposSubscriptionRequestProcessConfirmedAlarm()
self.tic()
for subscription_request in subscription_request_list:
self.assertTrue(
subscription_request.SubscriptionRequest_testPaymentBalance())
self.assertEqual('start_requested',
subscription_request.getAggregateValue().getSlapState())
self.checkStartedSubscriptionRequest(subscription_request,
default_email_text, self.subscription_condition)
def destroyAndCheckSubscription(self, default_email_text, subscription_server):
if self.skip_destroy_and_check:
return
subscription_request_list = self.getSubscriptionRequestList(
default_email_text, self.subscription_condition)
for subscription_request in subscription_request_list:
self.assertEqual('start_requested',
subscription_request.getAggregateValue().getSlapState())
# Destroy all instances and process
instance_tree = subscription_request.getAggregateValue()
instance_tree.InstanceTree_requestPerson('destroyed')
self.tic()
self.stepCallSlaposSubscriptionRequestProcessStartedAlarm()
self.tic()
self.checkStoppedSubscriptionRequest(subscription_request,
default_email_text, self.subscription_condition)
def _test_subscription_scenario(self, amount=1):
""" The admin creates an compute_node, user can request instances on it"""
self.subscription_server = self.createPublicServerForAdminUser()
# Call as anonymous... check response?
default_email_text = "abc%s@nexedi.com" % self.new_id
name="ABC %s" % self.new_id
self.requestAndCheckInstanceTree(
amount, name, default_email_text)
self.checkSubscriptionDeploymentAndSimulation(
default_email_text, self.subscription_server)
self.destroyAndCheckSubscription(
default_email_text, self.subscription_server
)
return default_email_text, name
def _test_subscription_scenario_with_existing_user(self, amount=1, language=None):
""" The admin creates an compute_node, user can request instances on it"""
# Call as anonymous... check response?
default_email_text = "abc%s@nexedi.com" % self.new_id
name="ABC %s" % self.new_id
project_reference = self.subscription_project.getReference()
self.login()
self.createNormalUser(default_email_text, name, language, self.subscription_project)
self.tic()
self.subscription_server = self.createPublicServerForAdminUser()
self.requestAndCheckInstanceTree(
amount, name, default_email_text)
self.checkSubscriptionDeploymentAndSimulation(
default_email_text, self.subscription_server)
subscription_request = self.getSubscriptionRequest(
default_email_text, self.subscription_condition)
self.assertEqual(self.normal_user,
subscription_request.getDestinationSectionValue())
self.destroyAndCheckSubscription(
default_email_text, self.subscription_server
)
return default_email_text, name
def _test_subscription_scenario_with_existing_user_with_non_subscription_request(self, amount=1, language=None):
# Call as anonymous... check response?
default_email_text = "abc%s@nexedi.com" % self.new_id
name="ABC %s" % self.new_id
self.login()
self.createNormalUser(default_email_text, name, language, self.subscription_project)
self.tic()
self.subscription_server = self.createPublicServerForAdminUser()
# Disable on this test the pricing on the template to not generate debt before
# them expected
supply_line = self.portal.sale_trade_condition_module.\
default_subscription_trade_condition.\
subscription_price_eur
previous_price = supply_line.getBasePrice()
supply_line.setBasePrice(0.0)
try:
self.login(self.normal_user.getUserId())
self.personRequestInstanceNotReady(
software_release=self.subscription_condition.getUrlString(),
software_type="default",
partition_reference="_test_subscription_scenario_with_existing_user_extra_instance",
project_reference=project_reference
)
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)
self.checkSubscriptionDeploymentAndSimulation(
default_email_text, self.subscription_server)
subscription_request = self.getSubscriptionRequest(
default_email_text, self.subscription_condition)
self.assertEqual(self.normal_user,
subscription_request.getDestinationSectionValue())
self.destroyAndCheckSubscription(
default_email_text, self.subscription_server
)
finally:
supply_line.setPrice(previous_price)
return default_email_text, name
def _test_two_subscription_scenario(self, amount=1, create_invitation=False,
max_invoice_delay=0, max_invoice_credit_eur=0.0, max_invoice_credit_cny=0.0):
""" The admin creates an compute_node, user can request instances on it"""
self.subscription_server = self.createPublicServerForAdminUser()
# Call as anonymous... check response?
default_email_text = "abc%s@nexedi.com" % self.new_id
name="ABC %s" % self.new_id
if create_invitation:
self.login()
self.cloud_invitation_token = self.makeCloudInvitationToken(
max_invoice_delay=max_invoice_delay,
max_invoice_credit_eur=max_invoice_credit_eur,
max_invoice_credit_cny=max_invoice_credit_cny
)
self.logout()
user_input_dict = {
"name": name,
"amount" : amount}
self._requestSubscription(
subscription_reference=self.subscription_condition.getReference(),
user_input_dict=user_input_dict,
email=default_email_text,
confirmation_required=False)
self.login()
# I'm not sure if this is realistic
self.tic()
first_subscription_request = self.getSubscriptionRequest(
default_email_text, self.subscription_condition)
self.checkDraftSubscriptionRequest(first_subscription_request,
default_email_text, self.subscription_condition, amount=amount)
# Check Payment and pay it.
self.checkAndPaySubscriptionPayment([first_subscription_request])
self.tic()
# Call alarm to check payment and invoice and move foward to planned.
self.stepCallSlaposSubscriptionRequestProcessDraftAlarm()
self.tic()
self.checkPlannedSubscriptionRequest(first_subscription_request,
default_email_text, self.subscription_condition)
# Call alarm to mark subscription request as ordered, bootstrap the user
# and check if email is sent, once done move to ordered state.
self.stepCallSlaposSubscriptionRequestProcessPlannedAlarm()
self.tic()
self.checkOrderedSubscriptionRequest(first_subscription_request,
default_email_text, self.subscription_condition)
if create_invitation:
self.login()
self.cloud_invitation_token = self.makeCloudInvitationToken(
max_invoice_delay=max_invoice_delay,
max_invoice_credit_eur=max_invoice_credit_eur,
max_invoice_credit_cny=max_invoice_credit_cny
)
self.logout()
# Request a second one, without require confirmation and verifing
# the second subscription request
user_input_dict = {
"name": name,
"amount" : amount}
self._requestSubscription(
subscription_reference=self.subscription_condition.getReference(),
user_input_dict=user_input_dict,
email=default_email_text,
confirmation_required=False)
self.login()
self.tic()
second_subscription_request = [ i for i in self.getSubscriptionRequestList(
default_email_text, self.subscription_condition) if i.getSimulationState() == "draft"][0]
self.checkDraftSubscriptionRequest(second_subscription_request,
default_email_text, self.subscription_condition)
# Check Payment and pay it.
self.checkAndPaySubscriptionPayment([second_subscription_request])
self.tic()
# Call alarm to check payment and invoice and move foward to planned.
self.stepCallSlaposSubscriptionRequestProcessDraftAlarm()
self.tic()
self.checkPlannedSubscriptionRequest(second_subscription_request,
default_email_text, self.subscription_condition)
# Call alarm to mark subscription request as ordered, bootstrap the user
# and check if email is sent, once done move to ordered state.
self.stepCallSlaposSubscriptionRequestProcessPlannedAlarm()
self.tic()
self.checkOrderedSubscriptionRequest(second_subscription_request,
default_email_text, self.subscription_condition,
notification_message="subscription_request-confirmation-without-password")
# Call alarm to make the request of the instance?
self.stepCallSlaposSubscriptionRequestProcessOrderedAlarm()
self.tic()
# The alarms might be called multiple times for move each step
self.stepCallSlaposSubscriptionRequestProcessOrderedAlarm()
self.tic()
self.checkSubscriptionDeploymentAndSimulation(
default_email_text, self.subscription_server)
self.destroyAndCheckSubscription(
default_email_text, self.subscription_server
)
return default_email_text, name
def _test_subscription_scenario_with_reversal_transaction(self, amount=1):
""" The admin creates an compute_node, user can request instances on it"""
self.subscription_server = self.createPublicServerForAdminUser()
# Call as anonymous... check response?
default_email_text = "abc%s@nexedi.com" % self.new_id
name="ABC %s" % self.new_id
self.requestAndCheckInstanceTree(amount, name, default_email_text)
self.checkSubscriptionDeploymentAndSimulationWithReversalTransaction(
default_email_text, self.subscription_server)
self.destroyAndCheckSubscription(
default_email_text, self.subscription_server
)
return default_email_text, name
def _test_second_month_scenario(self, default_email_text):
subscription_request_list = self.getSubscriptionRequestList(
default_email_text, self.subscription_condition)
# Ensure periodicity is correct
for subscription_request in subscription_request_list:
instance_tree = subscription_request.getAggregateValue()
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)))
self.addCleanup(self.unpinDateTime)
self._checkSecondMonthSimulation(subscription_request_list,
default_email_text, self.subscription_server)
self.skip_destroy_and_check = 0
self.destroyAndCheckSubscription(
default_email_text, self.subscription_server
)
class TestSlapOSSubscriptionScenario(TestSlapOSSubscriptionScenarioMixin):
def test_new_subscription_scenario_with_single_vm(self):
self._test_subscription_scenario(amount=1)
def test_subscription_scenario_with_single_vm(self):
self._test_subscription_scenario(amount=1)
def test_subscription_scenario_with_reversal_transaction(self):
self._test_subscription_scenario_with_reversal_transaction(amount=1)
def test_subscription_with_3_vms_scenario(self):
self._test_subscription_scenario(amount=3)
def test_two_subscription_scenario(self):
self._test_two_subscription_scenario(amount=1)
def test_subscription_scenario_with_existing_user(self):
self._test_subscription_scenario_with_existing_user(amount=1, language="en")
def test_subscription_scenario_with_existing_user_with_non_subscription_request(self):
self._test_subscription_scenario_with_existing_user_with_non_subscription_request(amount=1, language="en")
def test_subscription_scenario_with_existing_chinese_user(self):
# Messages are in english, when subscribed via english website. Even if the chinese language is
# defined at user level.
self._test_subscription_scenario_with_existing_user(amount=1, language="zh")
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from erp5.component.test.testSlapOSSubscriptionChineseScenario import TestSlapOSSubscriptionChineseScenarioMixin
class testSlapOSSubscriptionSecondMonthPaymentScenario(TestSlapOSSubscriptionChineseScenarioMixin):
def test_subscription_scenario_with_single_vm(self):
self.skip_destroy_and_check = 1
default_email_text, _ = self._test_subscription_scenario(amount=1)
self._test_second_month_scenario(default_email_text)
def test_subscription_with_3_vms_scenario(self):
self.skip_destroy_and_check = 1
default_email_text, _ = self._test_subscription_scenario(amount=3)
self._test_second_month_scenario(default_email_text)
def test_subscription_scenario_with_reversal_transaction(self):
self.skip_destroy_and_check = 1
default_email_text, _ = self._test_subscription_scenario_with_reversal_transaction(amount=1)
self._test_second_month_scenario(default_email_text)
def test_two_subscription_scenario(self):
self.skip_destroy_and_check = 1
default_email_text, _ = self._test_two_subscription_scenario(amount=1)
self._test_second_month_scenario(default_email_text)
def test_subscription_scenario_with_existing_chienese_user(self):
self.skip_destroy_and_check = 1
default_email_text, _ = self._test_subscription_scenario_with_existing_user(amount=1, language="zh")
self._test_second_month_scenario(default_email_text)
def test_subscription_scenario_with_existing_english_user(self):
self.skip_destroy_and_check = 1
default_email_text, _ = self._test_subscription_scenario_with_existing_user(amount=1, language="en")
self._test_second_month_scenario(default_email_text)
def test_subscription_scenario_with_existing_user_with_non_subscription_request(self):
self.skip_destroy_and_check = 1
default_email_text, _ = \
self._test_subscription_scenario_with_existing_user_with_non_subscription_request(amount=1, language="zh")
self._test_second_month_scenario(default_email_text)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionSecondMonthPaymentChineseScenario</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSubscriptionSecondMonthPaymentChineseScenario</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</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>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from erp5.component.test.testSlapOSSubscriptionScenario import TestSlapOSSubscriptionScenarioMixin
class testSlapOSSubscriptionSecondMonthPaymentScenario(TestSlapOSSubscriptionScenarioMixin):
def test_subscription_scenario_with_single_vm(self):
self.skip_destroy_and_check = 1
default_email_text, _ = self._test_subscription_scenario(amount=1)
self._test_second_month_scenario(default_email_text)
def test_subscription_with_3_vms_scenario(self):
self.skip_destroy_and_check = 1
default_email_text, _ = self._test_subscription_scenario(amount=3)
self._test_second_month_scenario(default_email_text)
def test_subscription_scenario_with_reversal_transaction(self):
self.skip_destroy_and_check = 1
default_email_text, _ = self._test_subscription_scenario_with_reversal_transaction(amount=1)
self._test_second_month_scenario(default_email_text)
def test_two_subscription_scenario(self):
self.skip_destroy_and_check = 1
default_email_text, _ = self._test_two_subscription_scenario(amount=1)
self._test_second_month_scenario(default_email_text)
def test_subscription_scenario_with_existing_chienese_user(self):
self.skip_destroy_and_check = 1
default_email_text, _ = self._test_subscription_scenario_with_existing_user(amount=1, language="zh")
self._test_second_month_scenario(default_email_text)
def test_subscription_scenario_with_existing_english_user(self):
self.skip_destroy_and_check = 1
default_email_text, _ = self._test_subscription_scenario_with_existing_user(amount=1, language="en")
self._test_second_month_scenario(default_email_text)
def test_subscription_scenario_with_existing_user_with_non_subscription_request(self):
self.skip_destroy_and_check = 1
default_email_text, _ = \
self._test_subscription_scenario_with_existing_user_with_non_subscription_request(amount=1, language="en")
self._test_second_month_scenario(default_email_text)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSubscriptionSecondMonthPaymentScenario</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSubscriptionSecondMonthPaymentScenario</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</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>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Interaction 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>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>slapos_slap_tool_interaction_workflow</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_subscription_request_interaction_workflow</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Interaction Workflow</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>SlapOS Subscription Request Interaction Workflow</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="Interaction Workflow Interaction" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>after_script/portal_workflow/slapos_subscription_request_interaction_workflow/script_Base_triggerOrphanedAlarm</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>interaction_ComputeNode_validate</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Interaction Workflow Interaction</string> </value>
</item>
<item>
<key> <string>portal_type_filter</string> </key>
<value>
<tuple>
<string>Compute Node</string>
<string>Instance Tree</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type_group_filter</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ComputeNode_validate</string> </value>
</item>
<item>
<key> <string>trigger_method_id</string> </key>
<value>
<tuple>
<string>validate</string>
</tuple>
</value>
</item>
<item>
<key> <string>trigger_once_per_transaction</string> </key>
<value> <int>1</int> </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="Interaction Workflow Interaction" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>after_script/portal_workflow/slapos_subscription_request_interaction_workflow/script_Base_triggerValidationAlarm</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>interaction_SubscriptionRequest_submit</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Interaction Workflow Interaction</string> </value>
</item>
<item>
<key> <string>portal_type_filter</string> </key>
<value>
<tuple>
<string>Subscription Request</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type_group_filter</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>trigger_method_id</string> </key>
<value>
<tuple>
<string>submit</string>
</tuple>
</value>
</item>
<item>
<key> <string>trigger_once_per_transaction</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
if alarm.getParentValue().isSubscribed() and not alarm.isActive() and alarm.isEnabled():
alarm.activate(queue='SQLQueue', **activate_kw).activeSense()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Script" module="erp5.portal_type"/>
</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>alarm, activate_kw</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_Alarm_safeTrigger</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Script</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
base = state_change['object']
portal = base.getPortalObject()
tag = script.id
base.reindexObject(activate_kw={'tag': tag})
context.Alarm_safeTrigger(portal.portal_alarms.slapos_subscription_request_create_from_orphaned_item, {'after_tag': tag})
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Script" module="erp5.portal_type"/>
</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>state_change</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_Base_triggerOrphanedAlarm</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Script</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
base = state_change['object']
portal = base.getPortalObject()
tag = script.id
base.reindexObject(activate_kw={'tag': tag})
context.Alarm_safeTrigger(portal.portal_alarms.slapos_subscription_request_validate_submitted, {'after_tag': tag})
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Script" module="erp5.portal_type"/>
</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>state_change</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_Base_triggerValidationAlarm</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Script</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -54,7 +54,58 @@
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
<dictionary>
<item>
<key> <string>Access contents information</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Assignee</string>
<string>Manager</string>
<string>Auditor</string>
</tuple>
</value>
</item>
<item>
<key> <string>Add portal content</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Delete objects</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Modify portal content</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>View</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Author</string>
<string>Associate</string>
<string>Assignee</string>
<string>Manager</string>
<string>Auditor</string>
</tuple>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
......
......@@ -20,11 +20,19 @@
<string>destination/portal_workflow/subscription_request_workflow/transition_cancel_action</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_delete</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_delete_action</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_plan</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_plan_action</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_submit</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_submit_action</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_validate</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_validate_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
......@@ -72,10 +80,8 @@
<key> <string>Access contents information</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Assignee</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
......@@ -85,9 +91,8 @@
<key> <string>Add portal content</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Assignee</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
......@@ -97,11 +102,10 @@
<key> <string>Delete objects</string> </key>
<value>
<tuple>
<string>Owner</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
<string>Owner</string>
<string>Assignor</string>
</tuple>
</value>
</item>
......@@ -109,11 +113,10 @@
<key> <string>Modify portal content</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
<string>Owner</string>
<string>Manager</string>
<string>Assignee</string>
</tuple>
</value>
</item>
......@@ -121,12 +124,10 @@
<key> <string>View</string> </key>
<value>
<tuple>
<string>Owner</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
<string>Owner</string>
<string>Assignor</string>
</tuple>
</value>
</item>
......
<?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/subscription_request_workflow/transition_validate</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_validate_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>state_invalidated</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>Closed</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>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
<string>Auditor</string>
<string>Associate</string>
</tuple>
</value>
</item>
<item>
<key> <string>Add portal content</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Delete objects</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Modify portal content</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>View</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
<string>Auditor</string>
<string>Associate</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/subscription_request_workflow/transition_deliver</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_deliver_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>state_stopped</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>Stopped</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>
</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/subscription_request_workflow/transition_cancel</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_cancel_action</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_validate</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_validate_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Tickets in this state are waiting for approval from the Ticket manager before being further processed.</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>state_submitted</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>Submitted</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>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
<string>Auditor</string>
</tuple>
</value>
</item>
<item>
<key> <string>Add portal content</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Delete objects</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Modify portal content</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>View</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
<string>Auditor</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/subscription_request_workflow/transition_invalidate</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_invalidate_action</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_validate</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_validate_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Support requests that is temporarily suspended (waiting for an answer from another department or from the customer) </string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>state_suspended</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>Suspended</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>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
<string>Auditor</string>
</tuple>
</value>
</item>
<item>
<key> <string>Add portal content</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Delete objects</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Modify portal content</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>View</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
<string>Auditor</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/subscription_request_workflow/transition_invalidate</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_invalidate_action</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_suspend</string>
<string>destination/portal_workflow/subscription_request_workflow/transition_suspend_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</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>Opened</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>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
<string>Auditor</string>
<string>Associate</string>
</tuple>
</value>
</item>
<item>
<key> <string>Add portal content</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Delete objects</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>Modify portal content</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>View</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
<string>Manager</string>
<string>Auditor</string>
<string>Associate</string>
</tuple>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -42,15 +42,17 @@
<item>
<key> <string>guard_permission</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
<tuple/>
</value>
</item>
<item>
<key> <string>guard_role</string> </key>
<value>
<tuple/>
<tuple>
<string>Owner</string>
<string>Assignee</string>
<string>Assignor</string>
</tuple>
</value>
</item>
<item>
......
......@@ -19,7 +19,6 @@
<value>
<tuple>
<string>action_type/workflow</string>
<string>before_script/portal_workflow/subscription_request_workflow/script_checkConsistency</string>
<string>after_script/portal_workflow/subscription_request_workflow/transition_cancel</string>
</tuple>
</value>
......@@ -39,16 +38,16 @@
<item>
<key> <string>guard_permission</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
<tuple/>
</value>
</item>
<item>
<key> <string>guard_role</string> </key>
<value>
<tuple>
<string>Assignor or Assignee</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Owner</string>
</tuple>
</value>
</item>
......@@ -68,7 +67,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>CancelAction</string> </value>
<value> <string>Cancel Action</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
......
<?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/subscription_request_workflow/state_confirmed</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_confirm</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>Confirm</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -39,6 +39,16 @@
<tuple/>
</value>
</item>
<item>
<key> <string>guard_role</string> </key>
<value>
<tuple>
<string>Owner</string>
<string>Assignee</string>
<string>Assignor</string>
</tuple>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value>
......
......@@ -51,6 +51,7 @@
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Owner</string>
</tuple>
</value>
</item>
......
<?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/subscription_request_workflow/state_delivered</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_deliver</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>Deliver</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -23,7 +23,7 @@
<value>
<tuple>
<string>action_type/workflow</string>
<string>destination/portal_workflow/subscription_request_workflow/state_started</string>
<string>destination/portal_workflow/subscription_request_workflow/state_invalidated</string>
</tuple>
</value>
</item>
......@@ -42,15 +42,16 @@
<item>
<key> <string>guard_permission</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
<tuple/>
</value>
</item>
<item>
<key> <string>guard_role</string> </key>
<value>
<tuple/>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
</tuple>
</value>
</item>
<item>
......@@ -61,7 +62,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_start</string> </value>
<value> <string>transition_invalidate</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......@@ -69,7 +70,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Start</string> </value>
<value> <string>Close Ticket</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
......
......@@ -8,19 +8,18 @@
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>%(content_url)s/Base_viewWorkflowActionDialog?workflow_action=confirm_action</string> </value>
<value> <string>%(content_url)s/Base_viewWorkflowActionDialog?workflow_action=validate_action</string> </value>
</item>
<item>
<key> <string>action_name</string> </key>
<value> <string>Confirm</string> </value>
<value> <string>Close Ticket</string> </value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/workflow</string>
<string>before_script/portal_workflow/subscription_request_workflow/script_checkConsistency</string>
<string>after_script/portal_workflow/subscription_request_workflow/transition_confirm</string>
<string>after_script/portal_workflow/subscription_request_workflow/transition_invalidate</string>
</tuple>
</value>
</item>
......@@ -59,7 +58,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_confirm_action</string> </value>
<value> <string>transition_invalidate_action</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......@@ -67,7 +66,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Confirm Action</string> </value>
<value> <string>Close Ticket Action</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
......
<?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/subscription_request_workflow/state_planned</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_plan</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>Plan</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>%(content_url)s/Base_viewWorkflowActionDialog?workflow_action=start_action</string> </value>
</item>
<item>
<key> <string>action_name</string> </key>
<value> <string>Start</string> </value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/workflow</string>
<string>before_script/portal_workflow/subscription_request_workflow/script_checkConsistency</string>
<string>after_script/portal_workflow/subscription_request_workflow/transition_start</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>
<string>Modify portal content</string>
</tuple>
</value>
</item>
<item>
<key> <string>guard_role</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
</tuple>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_start_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>Start Action</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> <string>%(content_url)s/Base_viewWorkflowActionDialog?workflow_action=stop_action</string> </value>
</item>
<item>
<key> <string>action_name</string> </key>
<value> <string>Stop</string> </value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/workflow</string>
<string>before_script/portal_workflow/subscription_request_workflow/script_checkConsistency</string>
<string>after_script/portal_workflow/subscription_request_workflow/transition_stop</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>
<string>Modify portal content</string>
</tuple>
</value>
</item>
<item>
<key> <string>guard_role</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
</tuple>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_stop_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>Stop Action</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/subscription_request_workflow/state_submitted</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>The submit logical transition sets the Ticket state to submitted.</string> </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>
<string>Assignee</string>
<string>Assignor</string>
</tuple>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_submit</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>Request Approval</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -8,27 +8,25 @@
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>%(content_url)s/Base_viewWorkflowActionDialog?workflow_action=order_action</string> </value>
<value> <string>%(content_url)s/Base_viewWorkflowActionDialog?workflow_action=submit_action</string> </value>
</item>
<item>
<key> <string>action_name</string> </key>
<value> <string>Order</string> </value>
<value> <string>Request Approval</string> </value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/workflow</string>
<string>after_script/portal_workflow/subscription_request_workflow/transition_submit</string>
<string>before_script/portal_workflow/subscription_request_workflow/script_checkConsistency</string>
<string>after_script/portal_workflow/subscription_request_workflow/transition_order</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
<value> <string>The submit logical transition sets the Ticket state to submitted.</string> </value>
</item>
<item>
<key> <string>guard_group</string> </key>
......@@ -39,17 +37,16 @@
<item>
<key> <string>guard_permission</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
<tuple/>
</value>
</item>
<item>
<key> <string>guard_role</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Owner</string>
<string>Assignee</string>
<string>Assignor</string>
</tuple>
</value>
</item>
......@@ -61,7 +58,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_order_action</string> </value>
<value> <string>transition_submit_action</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......@@ -69,7 +66,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Order Action</string> </value>
<value> <string>Request Approval Action</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
......
......@@ -23,7 +23,7 @@
<value>
<tuple>
<string>action_type/workflow</string>
<string>destination/portal_workflow/subscription_request_workflow/state_ordered</string>
<string>destination/portal_workflow/subscription_request_workflow/state_suspended</string>
</tuple>
</value>
</item>
......@@ -42,15 +42,16 @@
<item>
<key> <string>guard_permission</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
<tuple/>
</value>
</item>
<item>
<key> <string>guard_role</string> </key>
<value>
<tuple/>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
</tuple>
</value>
</item>
<item>
......@@ -61,7 +62,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_order</string> </value>
<value> <string>transition_suspend</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......@@ -69,7 +70,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Order</string> </value>
<value> <string>Suspend</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
......
......@@ -8,18 +8,18 @@
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>%(content_url)s/Base_viewWorkflowActionDialog?workflow_action=deliver_action</string> </value>
<value> <string>%(content_url)s/Base_viewWorkflowActionDialog?workflow_action=suspend_action</string> </value>
</item>
<item>
<key> <string>action_name</string> </key>
<value> <string>Deliver</string> </value>
<value> <string>Suspend</string> </value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/workflow</string>
<string>after_script/portal_workflow/subscription_request_workflow/transition_deliver</string>
<string>after_script/portal_workflow/subscription_request_workflow/transition_suspend</string>
</tuple>
</value>
</item>
......@@ -45,8 +45,8 @@
<key> <string>guard_role</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
<string>Assignor</string>
</tuple>
</value>
</item>
......@@ -58,7 +58,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_deliver_action</string> </value>
<value> <string>transition_suspend_action</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......@@ -66,7 +66,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Deliver Action</string> </value>
<value> <string>Suspend Action</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
......
......@@ -23,7 +23,7 @@
<value>
<tuple>
<string>action_type/workflow</string>
<string>destination/portal_workflow/subscription_request_workflow/state_stopped</string>
<string>destination/portal_workflow/subscription_request_workflow/state_validated</string>
</tuple>
</value>
</item>
......@@ -42,15 +42,15 @@
<item>
<key> <string>guard_permission</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
<tuple/>
</value>
</item>
<item>
<key> <string>guard_role</string> </key>
<value>
<tuple/>
<tuple>
<string>Assignor</string>
</tuple>
</value>
</item>
<item>
......@@ -61,7 +61,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_stop</string> </value>
<value> <string>transition_validate</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......@@ -69,7 +69,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Stop</string> </value>
<value> <string>Open Ticket</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
......
......@@ -8,11 +8,11 @@
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>%(content_url)s/Base_viewWorkflowActionDialog?workflow_action=plan_action</string> </value>
<value> <string>%(content_url)s/Base_viewWorkflowActionDialog?workflow_action=validate_action</string> </value>
</item>
<item>
<key> <string>action_name</string> </key>
<value> <string>Plan</string> </value>
<value> <string>Open Ticket</string> </value>
</item>
<item>
<key> <string>categories</string> </key>
......@@ -20,7 +20,7 @@
<tuple>
<string>action_type/workflow</string>
<string>before_script/portal_workflow/subscription_request_workflow/script_checkConsistency</string>
<string>after_script/portal_workflow/subscription_request_workflow/transition_plan</string>
<string>after_script/portal_workflow/subscription_request_workflow/transition_validate</string>
</tuple>
</value>
</item>
......@@ -47,7 +47,6 @@
<value>
<tuple>
<string>Assignor</string>
<string>Assignee</string>
</tuple>
</value>
</item>
......@@ -59,7 +58,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_plan_action</string> </value>
<value> <string>transition_validate_action</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......@@ -67,7 +66,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Plan Action</string> </value>
<value> <string>Open Ticket Action</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
......
......@@ -38,7 +38,7 @@
</item>
<item>
<key> <string>variable_default_expression</string> </key>
<value> <string>user/getUserName</string> </value>
<value> <string>user/getIdOrUserName</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -8,7 +8,7 @@
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>0</int> </value>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>description</string> </key>
......
......@@ -6,6 +6,8 @@ accounting_module/template_pre_payment_subscription_sale_invoice_transaction
accounting_module/template_pre_payment_subscription_sale_invoice_transaction/**
accounting_module/template_wechat_pre_payment_subscription_sale_invoice_transaction
accounting_module/template_wechat_pre_payment_subscription_sale_invoice_transaction/**
portal_alarms/slapos_subscription_request_create_from_orphaned_item
portal_alarms/slapos_subscription_request_process_**
portal_alarms/slapos_subscription_request_validate_submitted
portal_alarms/slapos_update_subscription_request_consistency_state
service_module/slapos_reservation_fee_2
service_module/slapos_reservation_fee_2
\ No newline at end of file
......@@ -12,7 +12,7 @@ Subscription Request | InstanceTree
Subscription Request | Person
Subscription Request | Price
Subscription Request | Reference
Subscription Request | SlapOSVirtualMasterSourceProjectConstraint
Subscription Request | SlapOSSubscriptionRequestConstraint
Subscription Request | Task
Subscription Request | Url
Subscription Request | VariationRange
\ No newline at end of file
Compute Node | slapos_subscription_request_interaction_workflow
Email | -coordinate_interaction_workflow
Instance Tree | slapos_subscription_request_interaction_workflow
Subscription Condition | commerce_validation_workflow
Subscription Condition | edit_workflow
Subscription Request | edit_workflow
Subscription Request | slapos_subscription_request_interaction_workflow
Subscription Request | subscription_request_workflow
\ No newline at end of file
SlapOSSubscriptionCondition
\ No newline at end of file
SlapOSSubscriptionCondition
SlapOSSubscriptionRequestConstraint
\ No newline at end of file
test.erp5.testSlapOSERP5VirtualMasterSubscriptionRequestScenario
test.erp5.testSlapOSSubscriptionAlarm
test.erp5.testSlapOSSubscriptionSkins
test.erp5.testSlapOSSubscriptionScenario
test.erp5.testSlapOSSubscriptionChineseScenario
test.erp5.testSlapOSSubscriptionNewTemplateScenario
test.erp5.testSlapOSSubscriptionNewTemplateChineseScenario
test.erp5.testSlapOSSubscriptionInvitationTokenScenario
test.erp5.testSlapOSSubscriptionInvitationTokenChineseScenario
test.erp5.testSlapOSSubscriptionSecondMonthPaymentScenario
test.erp5.testSlapOSSubscriptionSecondMonthPaymentChineseScenario
test.erp5.testSlapOSSubscriptionDualOrganisationScenario
test.erp5.testSlapOSSubscriptionCancellationScenario
test.erp5.testSlapOSSubscriptionCDNScenario
test.erp5.testSlapOSSubscriptionCDNChineseScenario
test.erp5.testSlapOSSubscriptionPerUserTradeConditionScenario
\ No newline at end of file
test.erp5.testSlapOSSubscriptionSkins
\ No newline at end of file
slapos_subscription_request_interaction_workflow
subscription_request_workflow
\ No newline at end of file
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