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

slapos_crm: support for the virtual master logic

Unify Ticket and Event creation, to ensure consistency on the different categories.
Use causality category instead of aggregate, to link the Ticket to the context document (instance, node, ...).
Aggregate must be used to define the item of the movement resource.

Monitoring: the tickets are managed by the virtual master admin,
who is responsible to provide a good service quality.
Admin will use their worklist to see the ongoing tickets.

Regularisation Request: this ticket is handle by the accounting team.
Unpaid invoice can be related to any kind of service (instance, node), which
have only 2 states from the ticket point of view: running or destroyed.
Only check the automated ledger, do not be impacted by other kind of invoices.

Prevent allocation if a customer has an ongoing Regularisation Request.

TODO: handle case of unpaid invoices for professional customers

Also in this commit:
* drop templates
* use interaction workflow to trigger alarms
parent 286a3bcc
......@@ -42,7 +42,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>jump_to_related_upgrade_decision_line</string> </value>
<value> <string>jump_to_related_upgrade_decision</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
......@@ -62,7 +62,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Related Upgrade Decision Line</string> </value>
<value> <string>Related Upgrade Decision</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
......@@ -81,7 +81,7 @@
<key> <string>text</string> </key>
<value> <string encoding="cdata"><![CDATA[
string:${object_url}/Base_jumpToRelatedObjectList?base_category=aggregate&portal_type=Upgrade%20Decision%20%Line&simulation_state=confirmed
string:${object_url}/Base_jumpToRelatedObjectList?base_category=aggregate&portal_type=Upgrade%20Decision&simulation_state=confirmed
]]></string> </value>
</item>
......@@ -96,7 +96,7 @@ string:${object_url}/Base_jumpToRelatedObjectList?base_category=aggregate&portal
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: portal.Base_checkPermission(\'subscription_request_module\', \'View\')</string> </value>
<value> <string>python: portal.Base_checkPermission(\'upgrade_decision_module\', \'View\')</string> </value>
</item>
</dictionary>
</pickle>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_user_pending_ticket_report</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>50.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Send Pending Ticket Report</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/Person_viewSlapOSPendingTicketDialog</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>update_destination_for_slapos</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>5.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Update Destination with all Slapos Users</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/SiteMessage_setSlapOSUserSourceAndDestinatationList</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: object.getSimulationState() in (\'draft\',)</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_jump</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_jump</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>jump_related_slapos_item</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>14.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Related Instance Tree, Compute Node or Software Installation</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string encoding="cdata"><![CDATA[
string:${object_url}/Base_jumpToRelationObject?base_category=aggregate&portal_type:list=Instance%20Tree&portal_type:list=Compute%20Node&portal_type:list=Software%20Installation
]]></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<module>
<category_list>
<category>business_application/crm</category>
</category_list>
<id>incident_response_module</id>
<permission_list>
<permission type='tuple'>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Message" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Delete_objects_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>destination/organisation_module/slapos</string>
</tuple>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>Web-TEMPLATE</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_crm_web_message_template</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Web Message</string> </value>
</item>
<item>
<key> <string>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>1359036420.0</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Template Web Message</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>edit_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>event_simulation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</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>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>0.0.0.0</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global id="3.1" name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global id="3.2" name="DateTime" module="DateTime.DateTime"/>
<global id="3.3" name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1359031769.94</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>0.0.0.0</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1359031776.9</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>924.10465.40641.49049</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1359031807.25</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>924.10466.8112.9318</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1359031829.43</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>924.10466.32280.31095</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1359031845.87</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>924.10466.50233.37427</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1359032280.41</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>924.10474.620.30208</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1359032348.56</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>924.10475.9470.10171</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1359032859.02</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>924.10483.42744.12902</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1359032919.02</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>937.58325.30915.36881</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1411993815.1</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>answer_portal_type</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>answer_template</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>create_event</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>respond_event_portal_type</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>respond_event_resource</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>respond_event_text_content</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>respond_event_title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>send_mail</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>simulation_state</string> </key>
<value> <string>draft</string> </value>
</item>
<item>
<key> <string>time</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>1359031770.04</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Person" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>__translation_dict</string> </key>
<value>
<dictionary/>
</value>
</item>
<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>default_reference</string> </key>
<value> <string>allocation_tester</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>first_name</string> </key>
<value> <string>Member</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>allocation_tester</string> </value>
</item>
<item>
<key> <string>last_name</string> </key>
<value> <string>Template</string> </value>
</item>
<item>
<key> <string>password</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Person</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Allocation tester</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>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value> <string>{SSHA}f1gAG3A53rfwjkLB/+Ex89MtocZz/4V9K4TZ</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -10,13 +10,17 @@
<key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_checkComputeNodeState</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Check if a public or a friend compute_node contacted master recently and create a ticket if the compute_node stops to contact master after some time.</string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>0</int> </value>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -10,13 +10,17 @@
<key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_checkInstanceTreeState</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Check and create a Ticket when an instance is partially allocated for more than 4 hours.</string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>0</int> </value>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -22,7 +22,7 @@
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>0</int> </value>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
......
<?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_sendPendingTicketReminder</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Send a Mail Message with a Reminder in case the user has Tickets to respond</string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_crm_send_pending_ticket_reminder</string> </value>
</item>
<item>
<key> <string>periodicity_hour</string> </key>
<value>
<tuple>
<int>6</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>
<int>0</int>
</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>1288051200.0</float>
<string>GMT</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>periodicity_week</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_week_day</string> </key>
<value>
<tuple>
<string>Tuesday</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Alarm</string> </value>
</item>
<item>
<key> <string>sense_method_id</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Check compute_node\'s state</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Regularisation Request" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>source_section/organisation_module/slapos</string>
<string>source_trade/organisation_module/slapos</string>
<string>source/organisation_module/slapos</string>
</tuple>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>RR-TEMPLATE</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_crm_regularisation_request_template</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Regularisation Request</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Template Regularisation Request</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>edit_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>ticket_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</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>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>0.0.0.0</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global id="3.1" name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global id="3.2" name="DateTime" module="DateTime.DateTime"/>
<global id="3.3" name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1362477335.16</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>925.6675.50749.18380</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1362477411.34</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>925.6676.56132.30788</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1362477422.73</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>simulation_state</string> </key>
<value> <string>draft</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global id="4.1" name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global id="4.2" name="DateTime" module="DateTime.DateTime"/>
<global id="4.3" name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1362477335.18</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>cancel_action</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>simulation_state</string> </key>
<value> <string>draft</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="4.1"/> </klass>
<tuple>
<reference id="4.2"/>
<reference id="4.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1362477430.94</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>cancel</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>simulation_state</string> </key>
<value> <string>cancelled</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="4.1"/> </klass>
<tuple>
<reference id="4.2"/>
<reference id="4.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1362477430.95</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Sale Trade Condition" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<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>categories</string> </key>
<value>
<tuple>
<string>source/organisation_module/slapos</string>
<string>source_section/organisation_module/slapos</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>slapos_ticket_trade_condition</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Sale Trade Condition</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>SlapOS Ticket Trade Condition</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>001</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Support Request" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>source_section/organisation_module/slapos</string>
<string>source_trade/organisation_module/slapos</string>
<string>source/organisation_module/slapos</string>
</tuple>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>SR-TEMPLATE</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_crm_support_request_template</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Support Request</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Template Support Request</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>edit_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>ticket_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</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>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>0.0.0.0</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global id="3.1" name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global id="3.2" name="DateTime" module="DateTime.DateTime"/>
<global id="3.3" name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1358438248.01</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>924.574.28012.55057</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1358438325.39</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>924.574.49639.23278</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1358438330.71</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>924.574.55464.10342</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1358438806.26</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>924.582.50574.51046</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<reference id="3.2"/>
<reference id="3.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1358438893.68</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>simulation_state</string> </key>
<value> <string>draft</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global id="4.1" name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global id="4.2" name="DateTime" module="DateTime.DateTime"/>
<global id="4.3" name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1358438248.02</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>cancel_action</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>simulation_state</string> </key>
<value> <string>draft</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="4.1"/> </klass>
<tuple>
<reference id="4.2"/>
<reference id="4.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1359118345.43</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>cancel</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>simulation_state</string> </key>
<value> <string>cancelled</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="4.1"/> </klass>
<tuple>
<reference id="4.2"/>
<reference id="4.3"/>
<none/>
</tuple>
<state>
<tuple>
<float>1359118345.44</float>
<string>GMT+1</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
</ZopeData>
<base_category_list>
<portal_type id="Compute Node">
<item>monitor_scope</item>
<item>upgrade_scope</item>
</portal_type>
<portal_type id="Incident Response">
<item>aggregate</item>
......@@ -12,7 +11,6 @@
</portal_type>
<portal_type id="Instance Tree">
<item>monitor_scope</item>
<item>upgrade_scope</item>
</portal_type>
<portal_type id="Regularisation Request">
<item>specialise</item>
......
......@@ -8,9 +8,6 @@
<portal_type id="Site Message">
<item>SlapOSEventConstraint</item>
</portal_type>
<portal_type id="Support Request">
<item>SlapOSSupportRequestConstraint</item>
</portal_type>
<portal_type id="Web Message">
<item>SlapOSEventConstraint</item>
</portal_type>
......
......@@ -12,10 +12,20 @@
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>acquire_local_roles</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>content_icon</string> </key>
<value> <string>folder_icon.gif</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>factory</string> </key>
<value> <string>addFolder</string> </value>
......@@ -32,6 +42,18 @@
<key> <string>id</string> </key>
<value> <string>Regularisation Request Module</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Base Type</string> </value>
......@@ -40,6 +62,12 @@
<key> <string>type_class</string> </key>
<value> <string>Folder</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
......
<workflow_chain>
<chain>
<type>Compute Node</type>
<workflow>slapos_crm_interaction_workflow</workflow>
</chain>
<chain>
<type>Incident Response</type>
<workflow>edit_workflow, ticket_workflow</workflow>
</chain>
<chain>
<type>Instance Tree</type>
<workflow>slapos_crm_interaction_workflow</workflow>
</chain>
<chain>
<type>Regularisation Request</type>
<workflow>edit_workflow, pricing_interaction_workflow, ticket_interaction_workflow, ticket_slap_interface_workflow, ticket_workflow</workflow>
<workflow>edit_workflow, pricing_interaction_workflow, slapos_crm_interaction_workflow, ticket_interaction_workflow, ticket_workflow</workflow>
</chain>
<chain>
<type>Sale Invoice Transaction</type>
<workflow>slapos_crm_interaction_workflow</workflow>
</chain>
<chain>
<type>Support Request</type>
<workflow>ticket_slap_interface_workflow</workflow>
<type>Sale Invoice Transaction Line</type>
<workflow>slapos_crm_interaction_workflow</workflow>
</chain>
</workflow_chain>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/boolean</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_cloud_contract_enabled_property</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: False</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/string</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_regularisation_request_template_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: \'\'</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/string</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_support_request_template_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: \'\'</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/string</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_web_message_template_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: \'\'</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Property Sheet" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SlapOSSupportRequestConstraint</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="Script 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>categories</string> </key>
<value>
<tuple>
<string>constraint_type/default</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>causality_source_destination_constraint_constraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Script Constraint</string> </value>
</item>
<item>
<key> <string>script_id</string> </key>
<value> <string>SupportRequest_checkCausalitySourceDestinationConsistency</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -3,28 +3,23 @@ portal = context.getPortalObject()
person_uid_list = []
for (_, brain) in enumerate(portal.portal_simulation.getInventoryList(
simulation_state=('stopped', 'delivered'),
parent_payment_mode_uid = [
portal.portal_categories.payment_mode.payzen.getUid(),
portal.portal_categories.payment_mode.wechat.getUid()],
group_by_mirror_section=True,
portal_type=portal.getPortalAccountingMovementTypeList(),
node_uid=[x.uid for x in context.Base_getReceivableAccountList()],
grouping_reference=None)):
node_uid=[x.uid for x in context.Base_getReceivableAccountList()] or -1,
parent__ledger__uid=portal.portal_categories.ledger.automated.getUid(),
grouping_reference=None
)):
payment_request_uid = brain.payment_request_uid
if not payment_request_uid:
payment_request_uid = brain.getObject().getExplanationUid()
payment_request = portal.portal_catalog.getObject(uid=payment_request_uid)
section_uid = payment_request.getDestinationSectionUid(portal_type="Person")
section_uid = brain.getDestinationSectionUid(portal_type="Person")
if section_uid is not None:
person_uid_list.append(section_uid)
portal.portal_catalog.searchAndActivate(
portal_type="Person",
validation_state="validated",
uid=person_uid_list,
method_id='Person_checkToCreateRegularisationRequest',
activate_kw={'tag': tag}
)
if person_uid_list:
portal.portal_catalog.searchAndActivate(
portal_type="Person",
validation_state="validated",
uid=person_uid_list,
method_id='Person_checkToCreateRegularisationRequest',
activate_kw={'tag': tag}
)
context.activate(after_tag=tag).getId()
portal = context.getPortalObject()
sub_tag = "RegularisationRequest_deleteInstanceTreeList"
portal.portal_catalog.searchAndActivate(
portal_type="Regularisation Request",
portal_type="Regularisation Request",
simulation_state=["suspended"],
default_resource_uid=portal.service_module.slapos_crm_delete_acknowledgement.getUid(),
resource__uid=portal.service_module.slapos_crm_delete_acknowledgement.getUid(),
method_id='RegularisationRequest_deleteInstanceTreeList',
method_args=(sub_tag,),
# Limit activity number, as method_id also calls searchAndActivate
......
portal = context.getPortalObject()
sub_tag = "RegularisationRequest_stopInstanceTreeList"
portal.portal_catalog.searchAndActivate(
portal_type="Regularisation Request",
portal_type="Regularisation Request",
simulation_state=["suspended"],
default_resource_uid=[
resource__uid=[
portal.service_module.slapos_crm_stop_acknowledgement.getUid(),
portal.service_module.slapos_crm_delete_reminder.getUid(),
portal.service_module.slapos_crm_delete_acknowledgement.getUid(),
......
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Regularisation Request",
portal_type="Regularisation Request",
simulation_state=["suspended"],
default_resource_uid=portal.service_module.slapos_crm_acknowledgement.getUid(),
resource__uid=portal.service_module.slapos_crm_acknowledgement.getUid(),
method_id='RegularisationRequest_triggerAcknowledgmentEscalation',
activate_kw={'tag': tag}
)
......
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Regularisation Request",
portal_type="Regularisation Request",
simulation_state=["suspended"],
default_resource_uid=portal.service_module.slapos_crm_stop_reminder.getUid(),
resource__uid=portal.service_module.slapos_crm_stop_reminder.getUid(),
method_id='RegularisationRequest_triggerStopReminderEscalation',
activate_kw={'tag': tag}
)
......
......@@ -15,7 +15,8 @@ if (slap_state in ['start_requested', 'stop_requested']):
software_type=instance_tree.getSourceReference(),
instance_xml=instance_tree.getTextContent(),
sla_xml=instance_tree.getSlaXml(),
shared=instance_tree.isRootSlave()
shared=instance_tree.isRootSlave(),
project_reference=instance_tree.getFollowUpReference()
)
return True
return False
......@@ -15,7 +15,8 @@ if (slap_state == 'start_requested'):
software_type=instance_tree.getSourceReference(),
instance_xml=instance_tree.getTextContent(),
sla_xml=instance_tree.getSlaXml(),
shared=instance_tree.isRootSlave()
shared=instance_tree.isRootSlave(),
project_reference=instance_tree.getFollowUpReference()
)
return True
return False
......@@ -16,27 +16,15 @@ ticket_portal_type = "Regularisation Request"
ticket = portal.portal_catalog.getResultValue(
portal_type=ticket_portal_type,
default_source_project_uid=person.getUid(),
destination__uid=person.getUid(),
simulation_state=['suspended', 'validated'],
)
if ticket is not None:
return ticket, None
outstanding_amount = person.Entity_statSlapOSOutstandingAmount()
# Amount to be ignored, as it comes from the first invoice generated
# after the subscription. We do not take it into account as no service
# was provided yet.
unpaid_invoice_amount = 0
for invoice in person.Person_getSubscriptionRequestFirstUnpaidInvoiceList():
unpaid_invoice_amount += invoice.getTotalPrice()
# It can't be smaller, we are considernig all open invoices are from unpaid_payment_amount
if round(float(outstanding_amount), 2) == round(float(unpaid_invoice_amount), 2):
return ticket, None
if int(outstanding_amount) > 0:
mail_message = None
if person.Entity_hasOutstandingAmount(ledger_uid=portal.portal_categories.ledger.automated.getUid()):
tag = "%s_addRegularisationRequest_inProgress" % person.getUid()
if (portal.portal_activities.countMessageWithTag(tag) > 0):
# The regularisation request is already under creation but can not be fetched from catalog
......@@ -47,59 +35,39 @@ if int(outstanding_amount) > 0:
person.serialize()
# Time to create the ticket
regularisation_request_template = portal.restrictedTraverse(
portal.portal_preferences.getPreferredRegularisationRequestTemplate())
ticket = regularisation_request_template.Base_createCloneDocument(batch_mode=1)
ticket.edit(
source_project_value=context,
title='Account regularisation expected for "%s"' % context.getTitle(),
destination_decision_value=context,
destination_value=context,
start_date=DateTime(),
resource=portal.portal_preferences.getPreferredRegularisationRequestResource(),
comment = 'New automatic ticket for %s' % context.getTitle()
ticket = context.Entity_createTicketFromTradeCondition(
portal.service_module.slapos_crm_monitoring.getRelativeUrl(),
'Account regularisation expected for "%s"' % context.getTitle(),
'',
portal_type='Regularisation Request',
comment=comment
)
ticket.validate(comment='New automatic ticket for %s' % context.getTitle())
ticket.suspend(comment='New automatic ticket for %s' % context.getTitle())
ticket.validate(comment=comment)
ticket.suspend(comment=comment)
ticket.reindexObject(activate_kw={'tag': tag})
# Notify using user's language
language = context.getLanguage("en")
notification_message = context.getPortalObject().portal_notifications.getDocumentValue(
reference="slapos-crm.create.regularisation.request",
language=language)
if notification_message is None:
subject = 'Invoice payment requested'
body = """Dear %s,
subject = 'Invoice payment requested'
body = """Dear %s,
A new invoice has been generated.
A new invoice has been generated.
You can access it in your invoice section at %s.
Regards,
The slapos team
""" % (context.getTitle(), portal.portal_preferences.getPreferredSlaposWebSiteUrl())
else:
notification_mapping_dict = {
'user_name': context.getTitle()}
subject = notification_message.getTitle()
# Preserve HTML else convert to text
if notification_message.getContentType() == "text/html":
body = notification_message.asEntireHTML(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
else:
body = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
notification_message_reference = "slapos-crm.create.regularisation.request"
mail_message = ticket.RegularisationRequest_checkToSendUniqEvent(
portal.portal_preferences.getPreferredRegularisationRequestResource(),
subject,
body,
'Requested manual payment.')
return ticket, mail_message
'Requested manual payment.',
notification_message=notification_message_reference,
substitution_method_parameter_dict={
'user_name': context.getTitle()
},
)
return ticket, None
return ticket, mail_message
portal = context.getPortalObject()
from erp5.component.module.DateUtils import addToDate
from Products.ZSQLCatalog.SQLCatalog import Query
from DateTime import DateTime
unpaid_list = []
subscription_request_list = portal.portal_catalog(
portal_type="Subscription Request",
simulation_state=["ordered", "confirmed"],
default_destination_section_uid=context.getUid(),
# Select "Subscription Request" with most likely unpaid invoices, recently generated.
creation_date=Query(creation_date=addToDate(DateTime(), to_add={'day': -20}), range="min"))
for subscription_request in subscription_request_list:
first_invoice = subscription_request.SubscriptionRequest_verifyPaymentBalanceIsReady()
if first_invoice is not None and not first_invoice.SaleInvoiceTransaction_isLettered():
unpaid_list.append(first_invoice)
return unpaid_list
......@@ -18,8 +18,8 @@ event_portal_type = "Mail Message"
event = portal.portal_catalog.getResultValue(
portal_type=event_portal_type,
default_resource_uid=service.getUid(),
default_follow_up_uid=ticket.getUid(),
resource__uid=service.getUid(),
follow_up__uid=ticket.getUid(),
)
if (event is None) and (ticket.getSimulationState() == 'suspended'):
......@@ -31,20 +31,15 @@ if (event is None) and (ticket.getSimulationState() == 'suspended'):
# Prevent concurrent transaction to create 2 events for the same ticket
ticket.edit(resource=service_relative_url)
event = portal.event_module.newContent(
portal_type=event_portal_type,
start_date=DateTime(),
destination=ticket.getDestination(),
follow_up=ticket.getRelativeUrl(),
source=context.getSource(),
title=title,
resource=service_relative_url,
event = ticket.Ticket_createProjectEvent(
title, 'outgoing', 'Mail Message',
service_relative_url,
text_content=text_content,
content_type='text/plain',
notification_message=notification_message,
substitution_method_parameter_dict=substitution_method_parameter_dict,
comment=comment
)
event.start(send_mail=True, comment=comment)
event.stop(comment=comment)
event.deliver(comment=comment)
event.reindexObject(activate_kw={'tag': tag})
return event
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>service_relative_url, title, text_content, comment, REQUEST=None</string> </value>
<value> <string>service_relative_url, title, text_content, comment, notification_message=None, substitution_method_parameter_dict=None, REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -11,14 +11,16 @@ event_portal_type = "Mail Message"
event = portal.portal_catalog.getResultValue(
portal_type=event_portal_type,
default_resource_uid=current_service.getUid(),
default_follow_up_uid=ticket.getUid(),
resource__uid=current_service.getUid(),
follow_up__uid=ticket.getUid(),
simulation_state="delivered",
)
if (ticket.getSimulationState() == 'suspended') and (event is not None) and (ticket.getResource() == current_service_relative_url):
if (DateTime() - event.getStartDate()) > delay_period_in_days:
ticket.RegularisationRequest_checkToSendUniqEvent(next_service_relative_url, title, text_content, comment)
ticket.RegularisationRequest_checkToSendUniqEvent(next_service_relative_url, title, text_content, comment,
notification_message=notification_message,
substitution_method_parameter_dict=substitution_method_parameter_dict)
return event.getRelativeUrl()
return None
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment, REQUEST=None</string> </value>
<value> <string>delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment, notification_message=None, substitution_method_parameter_dict=None, REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -4,19 +4,66 @@ if REQUEST is not None:
ticket = context
state = ticket.getSimulationState()
person = ticket.getSourceProjectValue(portal_type="Person")
person = ticket.getDestinationDecisionValue(portal_type="Person")
if (state == 'suspended') and \
(person is not None) and \
(ticket.getResource() == 'service_module/slapos_crm_delete_acknowledgement'):
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Instance Tree",
validation_state=["validated"],
default_destination_section_uid=person.getUid(),
method_id='InstanceTree_deleteFromRegularisationRequest',
method_args=(person.getRelativeUrl(),),
activate_kw={'tag': tag}
)
subscribed_item_list = []
ledger_uid = portal.portal_categories.ledger.automated.getUid()
# Gather the list of not paid services
for outstanding_amount in person.Entity_getOutstandingAmountList(
ledger_uid=ledger_uid,
include_planned=True
):
for outstanding_invoice in person.Entity_getOutstandingAmountList(
section_uid=outstanding_amount.getSourceSectionUid(),
resource_uid=outstanding_amount.getPriceCurrencyUid(),
ledger_uid=outstanding_amount.getLedgerUid(),
group_by_node=False
):
subscribed_item = None
for invoice_line in outstanding_invoice.getMovementList(
portal_type=['Invoice Line', 'Invoice Cell']
):
hosting_subscription = invoice_line.getAggregateValue(portal_type='Hosting Subscription')
if hosting_subscription is not None:
subscribed_item = invoice_line.getAggregateValue(portal_type=[
'Project',
'Instance Tree',
'Compute Node'
])
if subscribed_item is None:
raise NotImplementedError('Unhandled invoice line %s' % invoice_line.getRelativeUrl())
subscribed_item_list.append(subscribed_item)
if subscribed_item is None:
raise NotImplementedError('Unhandled invoice %s' % outstanding_invoice.getRelativeUrl())
for subscribed_item in subscribed_item_list:
if ((subscribed_item.getPortalType() == 'Compute Node') and
(subscribed_item.getAllocationScope() != 'close/forever')):
# allow cleaning up the compute node even if deleted
subscribed_item.edit(allocation_scope='close/forever')
elif (subscribed_item.getPortalType() == 'Instance Tree'):
# change the slap state to deleted, to allow propagation of the state
# even on remote node
subscribed_item.InstanceTree_deleteFromRegularisationRequest(person.getRelativeUrl())
elif ((subscribed_item.getPortalType() == 'Project') and
(subscribed_item.getValidationState() != 'invalidated')):
# do not close the project until all node and instance trees are corrected deleted
can_invalidate_project = True
for other_item in portal.portal_catalog(
portal_type=['Compute Node', 'Instance Tree'],
follow_up__uid=subscribed_item.getUid()
):
if other_item.getValidationState() not in ['invalidated', 'archived']:
can_invalidate_project = False
subscribed_item_list.append(other_item)
if can_invalidate_project:
subscribed_item.invalidate(comment='Not paid')
return True
return False
......@@ -3,26 +3,10 @@ if REQUEST is not None:
raise Unauthorized
state = context.getSimulationState()
person = context.getSourceProjectValue(portal_type="Person")
person = context.getDestinationDecisionValue(portal_type="Person")
if (state not in ('suspended', 'validated')) or \
(person is None):
return
outstanding_amount = person.Entity_statSlapOSOutstandingAmount()
# Amount to be ignored, as it comes from the first invoice generated
# after the subscription. We do not take it into account as no service
# was provided yet.
unpaid_invoice_amount = 0
for invoice in person.Person_getSubscriptionRequestFirstUnpaidInvoiceList():
unpaid_invoice_amount += invoice.getTotalPrice()
# It can't be smaller, we are considernig all open invoices are from unpaid_payment_amount
if round(float(outstanding_amount), 2) == round(float(unpaid_invoice_amount), 2):
context.invalidate(comment="Automatically disabled as balance is %s" % outstanding_amount)
return
if (int(outstanding_amount) > 0):
return
context.invalidate(comment="Automatically disabled as balance is %s" % outstanding_amount)
if not person.Entity_hasOutstandingAmount(ledger_uid=context.getPortalObject().portal_categories.ledger.automated.getUid()):
context.invalidate(comment="Automatically disabled as balance is ok")
......@@ -4,16 +4,16 @@ if REQUEST is not None:
ticket = context
state = ticket.getSimulationState()
person = ticket.getSourceProjectValue(portal_type="Person")
person = ticket.getDestinationDecisionValue(portal_type="Person")
if (state == 'suspended') and \
(person is not None) and \
(ticket.getResource() in ['service_module/slapos_crm_stop_acknowledgement', 'service_module/slapos_crm_delete_reminder', 'service_module/slapos_crm_delete_acknowledgement']):
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Instance Tree",
validation_state=["validated"],
default_destination_section_uid=person.getUid(),
destination_section__uid=person.getUid(),
method_id='InstanceTree_stopFromRegularisationRequest',
method_args=(person.getRelativeUrl(),),
activate_kw={'tag': tag}
......
......@@ -2,19 +2,10 @@ from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
portal = context.getPortalObject()
ndays = 15
language = "en"
recipient = context.getDestinationSectionValue()
if recipient is not None:
language = recipient.getLanguage("en")
notification_message = portal.portal_notifications.getDocumentValue(
language=language, reference="slapos-crm.acknowledgment.escalation")
if notification_message is None:
subject = 'Reminder: invoice payment requested'
body = """Dear user,
subject = 'Reminder: invoice payment requested'
body = """Dear user,
We would like to remind you the unpaid invoice you have on %s.
If no payment is done during the coming days, we will stop all your current instances to free some hardware resources.
......@@ -23,26 +14,16 @@ Regards,
The slapos team
""" % context.getPortalObject().portal_preferences.getPreferredSlaposWebSiteUrl()
else:
notification_mapping_dict = {
'user_name': context.getDestinationSectionTitle(),
'days': ndays}
subject = notification_message.getTitle()
# Preserve HTML else convert to text
if notification_message.getContentType() == "text/html":
body = notification_message.asEntireHTML(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
else:
body = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
return context.RegularisationRequest_checkToTriggerNextEscalationStep(
delay_period_in_days=ndays,
current_service_relative_url='service_module/slapos_crm_acknowledgement',
next_service_relative_url='service_module/slapos_crm_stop_reminder',
next_service_relative_url='service_module/slapos_crm_delete_reminder',
title=subject,
text_content=body,
comment='Stopping reminder.',
notification_message="slapos-crm.acknowledgment.escalation",
substitution_method_parameter_dict={
'user_name': context.getDestinationSectionTitle(),
'days': ndays
}
)
......@@ -2,19 +2,10 @@ from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
portal = context.getPortalObject()
ndays = 10
language = "en"
recipient = context.getDestinationSectionValue()
if recipient is not None:
language = recipient.getLanguage("en")
notification_message = portal.portal_notifications.getDocumentValue(
language=language, reference="slapos-crm.delete.reminder.escalation")
if notification_message is None:
subject = 'Acknowledgment: instances deleted'
body = """Dear user,
subject = 'Acknowledgment: instances deleted'
body = """Dear user,
Despite our last reminder, you still have an unpaid invoice on %s.
We will now delete all your instances.
......@@ -22,20 +13,6 @@ We will now delete all your instances.
Regards,
The slapos team
""" % context.getPortalObject().portal_preferences.getPreferredSlaposWebSiteUrl()
else:
notification_mapping_dict = {
'user_name': context.getDestinationSectionTitle(),
'days': ndays}
subject = notification_message.getTitle()
# Preserve HTML else convert to text
if notification_message.getContentType() == "text/html":
body = notification_message.asEntireHTML(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
else:
body = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
return context.RegularisationRequest_checkToTriggerNextEscalationStep(
delay_period_in_days=ndays,
......@@ -44,4 +21,9 @@ return context.RegularisationRequest_checkToTriggerNextEscalationStep(
title=subject,
text_content=body,
comment='Deleting acknowledgment.',
notification_message="slapos-crm.delete.reminder.escalation",
substitution_method_parameter_dict={
'user_name': context.getDestinationSectionTitle(),
'days': ndays
}
)
......@@ -2,19 +2,9 @@ from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
portal = context.getPortalObject()
ndays = 7
language = "en"
recipient = context.getDestinationSectionValue()
if recipient is not None:
language = recipient.getLanguage("en")
notification_message = portal.portal_notifications.getDocumentValue(
language=language, reference="slapos-crm.stop.acknowledgment.escalation")
if notification_message is None:
subject = 'Last reminder: invoice payment requested'
body = """Dear user,
subject = 'Last reminder: invoice payment requested'
body = """Dear user,
We would like to remind you the unpaid invoice you have on %s.
If no payment is done during the coming days, we will delete all your instances.
......@@ -22,20 +12,6 @@ If no payment is done during the coming days, we will delete all your instances.
Regards,
The slapos team
""" % context.getPortalObject().portal_preferences.getPreferredSlaposWebSiteUrl()
else:
notification_mapping_dict = {
'user_name': context.getDestinationSectionTitle(),
'days': ndays}
subject = notification_message.getTitle()
# Preserve HTML else convert to text
if notification_message.getContentType() == "text/html":
body = notification_message.asEntireHTML(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
else:
body = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
return context.RegularisationRequest_checkToTriggerNextEscalationStep(
delay_period_in_days=ndays,
......@@ -44,4 +20,9 @@ return context.RegularisationRequest_checkToTriggerNextEscalationStep(
title=subject,
text_content=body,
comment='Deleting reminder.',
notification_message="slapos-crm.stop.acknowledgment.escalation",
substitution_method_parameter_dict={
'user_name': context.getDestinationSectionTitle(),
'days': ndays
}
)
......@@ -2,19 +2,10 @@ from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
portal = context.getPortalObject()
ndays = 7
language = "en"
recipient = context.getDestinationSectionValue()
if recipient is not None:
language = recipient.getLanguage("en")
notification_message = portal.portal_notifications.getDocumentValue(
language=language, reference="slapos-crm.stop.reminder.escalation")
if notification_message is None:
subject = 'Acknowledgment: instances stopped'
body = """Dear user,
subject = 'Acknowledgment: instances stopped'
body = """Dear user,
Despite our last reminder, you still have an unpaid invoice on %s.
We will now stop all your current instances to free some hardware resources.
......@@ -22,20 +13,6 @@ We will now stop all your current instances to free some hardware resources.
Regards,
The slapos team
""" % context.getPortalObject().portal_preferences.getPreferredSlaposWebSiteUrl()
else:
notification_mapping_dict = {
'user_name': context.getDestinationSectionTitle(),
'days': ndays}
subject = notification_message.getTitle()
# Preserve HTML else convert to text
if notification_message.getContentType() == "text/html":
body = notification_message.asEntireHTML(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
else:
body = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
return context.RegularisationRequest_checkToTriggerNextEscalationStep(
delay_period_in_days=ndays,
......@@ -44,4 +21,9 @@ return context.RegularisationRequest_checkToTriggerNextEscalationStep(
title=subject,
text_content=body,
comment='Stopping acknowledgment.',
notification_message="slapos-crm.stop.reminder.escalation",
substitution_method_parameter_dict={
'user_name': context.getDestinationSectionTitle(),
'days': ndays
}
)
......@@ -9,11 +9,11 @@ monitor_enabled_category = portal.restrictedTraverse(
if monitor_enabled_category is not None:
portal.portal_catalog.searchAndActivate(
portal_type = 'Compute Node',
validation_state = 'validated',
default_monitor_scope_uid = monitor_enabled_category.getUid(),
method_id = 'ComputeNode_checkState',
activate_kw = {'tag':tag}
portal_type='Compute Node',
validation_state='validated',
monitor_scope__uid=monitor_enabled_category.getUid(),
method_id='ComputeNode_checkState',
activate_kw={'tag':tag}
)
context.activate(after_tag=tag).getId()
......@@ -9,11 +9,11 @@ monitor_enabled_category = portal.restrictedTraverse(
if monitor_enabled_category is not None:
portal.portal_catalog.searchAndActivate(
portal_type = 'Compute Node',
validation_state = 'validated',
default_monitor_scope_uid = monitor_enabled_category.getUid(),
method_id = 'ComputeNode_checkSoftwareInstallationState',
activate_kw = {'tag':tag}
portal_type='Compute Node',
validation_state='validated',
monitor_scope__uid=monitor_enabled_category.getUid(),
method_id='ComputeNode_checkSoftwareInstallationState',
activate_kw={'tag':tag}
)
context.activate(after_tag=tag).getId()
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type = 'Person',
method_id = 'Person_sendPendingTicketReminder',
activate_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_sendPendingTicketReminder</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -3,11 +3,11 @@ portal = context.getPortalObject()
default_resource_uid = portal.restrictedTraverse("service_module/slapos_crm_monitoring", None).getUid()
portal.portal_catalog.searchAndActivate(
portal_type='Support Request',
simulation_state=['validated', 'suspended'],
default_resource_uid=default_resource_uid,
default_aggregate_portal_type=["Instance Tree"],
simulation_state=['submitted', 'validated', 'suspended'],
resource__uid=default_resource_uid,
aggregate__portal_type=["Instance Tree"],
method_id='SupportRequest_updateMonitoringState',
activate_kw = {'tag':tag}
activate_kw={'tag':tag}
)
context.activate(after_tag=tag).getId()
from DateTime import DateTime
portal = context.getPortalObject()
person = context.getSourceAdministrationValue(portal_type="Person")
if not person or \
context.getMonitorScope() == "disabled" or \
if (context.getMonitorScope() == "disabled") or \
portal.ERP5Site_isSupportRequestCreationClosed():
return
return
software_installation_list = portal.portal_catalog(
portal_type='Software Installation',
default_aggregate_uid=context.getUid(),
aggregate__uid=context.getUid(),
validation_state='validated',
sort_on=(('creation_date', 'DESC'),)
)
......@@ -26,15 +24,20 @@ for software_installation in software_installation_list:
# Give it 12 hours to deploy.
continue
if software_installation.getSlapState() != 'start_requested':
continue
reference = software_installation.getReference()
d = software_installation.getAccessStatus()
if d.get("no_data", None) == 1:
should_notify = True
last_contact = "No Contact Information"
ticket_title = "[MONITORING] No information for %s on %s" % (reference, compute_node_reference)
description = "The software release %s did not started to build on %s since %s" % \
(software_installation.getUrlString(), compute_node_title, software_installation.getCreationDate())
else:
last_contact = DateTime(d.get('created_at'))
if d.get("text").startswith("building"):
if d.get("text").startswith("#building"):
should_notify = True
ticket_title = "[MONITORING] %s is building for too long on %s" % (reference, compute_node_reference)
description = "The software release %s is building for mode them 12 hours on %s, started on %s" % \
......@@ -49,42 +52,34 @@ for software_installation in software_installation_list:
(software_installation.getUrlString(), compute_node_title, software_installation.getCreationDate())
if should_notify:
support_request = person.Base_getSupportRequestInProgress(
title=ticket_title,
aggregate=software_installation.getRelativeUrl())
if support_request is None:
person.notify(support_request_title=ticket_title,
support_request_description=description,
aggregate=software_installation.getRelativeUrl())
support_request_relative_url = context.REQUEST.get("support_request_relative_url")
if support_request_relative_url is None:
return
project = context.getFollowUpValue()
support_request = project.Project_createSupportRequestWithCausality(
ticket_title,
description,
causality=context.getRelativeUrl(),
destination_decision=project.getDestination()
)
support_request = portal.restrictedTraverse(support_request_relative_url)
if support_request is None:
return
# Send Notification message
notification_reference = 'slapos-crm-compute_node_software_installation_state.notification'
notification_message = portal.portal_notifications.getDocumentValue(
reference=notification_reference)
if notification_message is None:
message = """%s""" % description
else:
mapping_dict = {'compute_node_title':context.getTitle(),
'compute_node_id':reference,
'last_contact':last_contact}
message = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':mapping_dict})
notification_message_reference = 'slapos-crm-compute_node_software_installation_state.notification'
event = support_request.SupportRequest_getLastEvent(ticket_title)
if event is None:
support_request.notify(message_title=ticket_title, message=message)
support_request.Ticket_createProjectEvent(
ticket_title, 'outgoing', 'Web Message',
portal.service_module.slapos_crm_information.getRelativeUrl(),
text_content=description,
content_type='text/plain',
notification_message=notification_message_reference,
#language=XXX,
substitution_method_parameter_dict={
'compute_node_title':context.getTitle(),
'compute_node_id':reference,
'last_contact':last_contact
}
)
support_request_list.append(support_request)
......
from DateTime import DateTime
portal = context.getPortalObject()
person = context.getSourceAdministrationValue(portal_type="Person")
if not person or \
context.getMonitorScope() == "disabled" or \
if (context.getMonitorScope() == "disabled") or \
portal.ERP5Site_isSupportRequestCreationClosed():
return
......@@ -56,7 +54,7 @@ if not should_notify:
if compute_partition_uid_list:
instance_list = portal.portal_catalog(
portal_type='Software Instance',
default_aggregate_uid=compute_partition_uid_list)
aggregate__uid=compute_partition_uid_list)
if instance_list:
should_notify = True
......@@ -78,45 +76,36 @@ if not should_notify:
context.getTitle(), context.getReference(), last_contact)
if should_notify:
support_request = person.Base_getSupportRequestInProgress(
title=node_ticket_title,
aggregate=context.getRelativeUrl())
support_request = context.Base_getSupportRequestInProgress(
title=node_ticket_title)
if support_request is None:
support_request = person.Base_getSupportRequestInProgress(
title=ticket_title,
aggregate=context.getRelativeUrl())
if support_request is None:
person.notify(support_request_title=ticket_title,
support_request_description=description,
aggregate=context.getRelativeUrl())
support_request_relative_url = context.REQUEST.get("support_request_relative_url")
if support_request_relative_url is None:
return
support_request = portal.restrictedTraverse(support_request_relative_url)
project = context.getFollowUpValue()
support_request = project.Project_createSupportRequestWithCausality(
ticket_title,
description,
causality=context.getRelativeUrl(),
destination_decision=project.getDestination()
)
if support_request is None:
return
# Send Notification message
notification_message = portal.portal_notifications.getDocumentValue(
reference=notification_message_reference)
if notification_message is None:
message = """%s""" % description
else:
mapping_dict = {'compute_node_title':context.getTitle(),
'compute_node_id':reference,
'last_contact':last_contact,
'issue_document_reference': issue_document_reference}
message = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict': mapping_dict})
event = support_request.SupportRequest_getLastEvent(ticket_title)
if event is None:
support_request.notify(message_title=ticket_title, message=message)
support_request.Ticket_createProjectEvent(
ticket_title, 'outgoing', 'Web Message',
portal.service_module.slapos_crm_information.getRelativeUrl(),
text_content=description,
content_type='text/plain',
notification_message=notification_message_reference,
#language=XXX,
substitution_method_parameter_dict={
'compute_node_title':context.getTitle(),
'compute_node_id':reference,
'last_contact':last_contact,
'issue_document_reference': issue_document_reference
}
)
return support_request
portal = context.getPortalObject()
compute_node = context
now_date = DateTime()
......@@ -15,13 +14,4 @@ if message_dict.has_key('created_at'):
contact_date = DateTime(message_dict.get('created_at').encode('utf-8'))
return (now_date - contact_date) < maximum_days
# If no access status information, check in consumption report
for sale_packing_list in portal.portal_catalog(
portal_type="Sale Packing List Line",
simulation_state="delivered",
default_aggregate_uid=compute_node.getUid(),
sort_on=[('movement.start_date', 'DESC')],
limit=1):
return (now_date - sale_packing_list.getStartDate()) < maximum_days
return False
......@@ -96,8 +96,8 @@
<key> <string>left</string> </key>
<value>
<list>
<string>my_reference</string>
<string>my_title</string>
<string>my_reference</string>
</list>
</value>
</item>
......
......@@ -2,19 +2,20 @@ from Products.ERP5Type.Cache import CachingMethod
portal = context.getPortalObject()
def isSupportRequestCreationClosed(destination_decision=None):
limit = portal.portal_preferences.getPreferredSupportRequestCreationLimit(5)
limit = int(portal.portal_preferences.getPreferredSupportRequestCreationLimit(5))
kw = {}
kw['limit'] = limit
kw['portal_type'] = 'Support Request'
kw['simulation_state'] = ["validated","submitted"]
kw['default_resource_uid'] = portal.service_module.slapos_crm_monitoring.getUid()
kw = {
'limit': limit,
'portal_type': 'Support Request',
'simulation_state': ["validated", "submitted"],
'resource__uid': portal.service_module.slapos_crm_monitoring.getUid()
}
if destination_decision:
kw['default_destination_decision_uid'] = context.restrictedTraverse(
kw['destination_decision__uid'] = context.restrictedTraverse(
destination_decision).getUid()
support_request_amount = context.portal_catalog.countResults(**kw)[0][0]
return support_request_amount >= int(limit)
support_request_amount_list = context.portal_catalog(**kw)
return limit <= len(support_request_amount_list)
return CachingMethod(isSupportRequestCreationClosed,
......
portal = context.getPortalObject()
destination_decision_value = context
# Create a temp Sale Order to find the trade condition
now = DateTime()
module = portal.portal_trash
tmp_sale_order = module.newContent(
portal_type='Sale Order',
temp_object=True,
trade_condition_type="ticket",
start_date=now,
destination_value=destination_decision_value,
destination_decision_value=destination_decision_value,
source_project=source_project,
ledger_value=portal.portal_categories.ledger.automated
)
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 Support Request')
"""
resource = portal.restrictedTraverse(resource)
ticket = portal.getDefaultModule(portal_type).newContent(
portal_type=portal_type,
title=title,
description=text_content,
start_date=tmp_sale_order.getStartDate(),
source=tmp_sale_order.getSource(),
source_section=tmp_sale_order.getSourceSection(),
source_project=tmp_sale_order.getSourceProject(),
destination=tmp_sale_order.getDestination(),
destination_section=tmp_sale_order.getDestinationSection(),
destination_project=tmp_sale_order.getDestinationProject(),
destination_decision=tmp_sale_order.getDestinationDecision(),
specialise=tmp_sale_order.getSpecialise(),
causality=causality,
# Ensure resoure is Monitoring
resource_value=resource,
quantity_unit=resource.getQuantityUnit(),
base_contribution_list=resource.getBaseContributionList(),
use=resource.getUse(),
quantity=1,
price=0
)
ticket.submit(comment=comment)
return ticket
......@@ -50,11 +50,11 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>instance, notification_message_reference</string> </value>
<value> <string>resource, title, text_content, portal_type=\'Support Request\', source_project=None, causality=None, comment=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>InstanceTree_createSupportRequestEvent</string> </value>
<value> <string>Entity_createTicketFromTradeCondition</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -4,10 +4,6 @@ from erp5.component.module.DateUtils import addToDate
instance_tree = context
portal = context.getPortalObject()
if instance_tree.getMonitorScope() == "disabled":
# Don't generate ticket if Monitor Scope is marked to disable
return
if portal.ERP5Site_isSupportRequestCreationClosed():
# Stop ticket creation
return
......@@ -20,14 +16,11 @@ if (date_check_limit - instance_tree.getCreationDate()) < 0:
software_instance_list = context.portal_catalog(
portal_type=["Software Instance", "Slave Instance"],
specialise_uid=instance_tree.getUid(),
specialise__uid=instance_tree.getUid(),
**{"slapos_item.slap_state": ["start_requested"]})
has_newest_allocated_instance = False
has_unallocated_instance = False
failing_instance = None
# Check if at least one software Instance is Allocated
notification_message_reference = None
for instance in software_instance_list:
if (date_check_limit - instance.getCreationDate()) < 0:
continue
......@@ -36,19 +29,48 @@ for instance in software_instance_list:
continue
compute_partition = instance.getAggregateValue()
if compute_partition is not None:
has_newest_allocated_instance = True
if instance.getPortalType() == "Software Instance" and \
compute_partition.getParentValue().getMonitorScope() == "enabled" and \
instance.SoftwareInstance_hasReportedError(tolerance=30):
return context.InstanceTree_createSupportRequestEvent(
instance, 'slapos-crm-instance-tree-instance-state.notification')
else:
has_unallocated_instance = True
failing_instance = instance
if has_unallocated_instance and has_newest_allocated_instance:
return context.InstanceTree_createSupportRequestEvent(
failing_instance, 'slapos-crm-instance-tree-instance-allocation.notification')
return
if compute_partition is None:
notification_message_reference = 'slapos-crm-instance-tree-instance-allocation.notification'
elif (instance.getPortalType() == "Software Instance") and \
(compute_partition.getParentValue().getMonitorScope() == "enabled") and \
instance.SoftwareInstance_hasReportedError(tolerance=30):
notification_message_reference = 'slapos-crm-instance-tree-instance-state.notification'
if notification_message_reference is not None:
ticket_title = "Instance Tree %s is failing." % context.getTitle()
error_message = instance.SoftwareInstance_hasReportedError(include_message=True)
description = "%s contains software instances which are unallocated or reporting errors." % (
context.getTitle())
if error_message:
description += "\n\nMessage: %s" % str(error_message)
else:
error_message = "No message!"
project = context.getFollowUpValue()
support_request = project.Project_createSupportRequestWithCausality(
ticket_title,
description,
causality=context.getRelativeUrl(),
destination_decision=context.getDestinationSection()
)
if support_request is None:
return
event = support_request.SupportRequest_getLastEvent(ticket_title)
if event is None:
support_request.Ticket_createProjectEvent(
ticket_title, 'outgoing', 'Web Message',
portal.service_module.slapos_crm_information.getRelativeUrl(),
text_content=description,
content_type='text/plain',
notification_message=notification_message_reference,
#language=XXX,
substitution_method_parameter_dict={
'instance_tree_title':context.getTitle(),
'instance': instance.getTitle(),
'error_text': error_message
}
)
return
portal = context.getPortalObject()
person = context.getDestinationSectionValue()
if person is None or portal.ERP5Site_isSupportRequestCreationClosed(person.getRelativeUrl()):
# Stop ticket creation
return
ticket_title = "Instance Tree %s is failing." % context.getTitle()
error_message = instance.SoftwareInstance_hasReportedError(include_message=True)
description = "%s contains software instances which are unallocated or reporting errors." % (
context.getTitle())
if error_message:
description += "\n\nMessage: %s" % error_message
else:
error_message = "No message!"
support_request = person.Base_getSupportRequestInProgress(
title=ticket_title,
aggregate=context.getRelativeUrl())
if support_request is None:
person.notify(support_request_title=ticket_title,
support_request_description=description,
aggregate=context.getRelativeUrl())
support_request_relative_url = context.REQUEST.get("support_request_relative_url")
if support_request_relative_url is None:
return
support_request = portal.restrictedTraverse(support_request_relative_url)
if support_request is None:
return
if support_request.getSimulationState() not in ["validated", "suspended"]:
support_request.validate()
# Send Notification message
message = description
notification_message = portal.portal_notifications.getDocumentValue(
reference=notification_message_reference)
if notification_message is not None:
mapping_dict = {'instance_tree_title':context.getTitle(),
'instance': instance.getTitle(),
'error_text': error_message}
message = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':mapping_dict})
event = support_request.SupportRequest_getLastEvent(ticket_title)
if event is None:
support_request.notify(message_title=ticket_title, message=message)
return context.REQUEST.get("ticket_notified_item")
from Products.ZSQLCatalog.SQLCatalog import ComplexQuery, SimpleQuery
portal = context.getPortalObject()
person_uid = context.getUid()
query = ComplexQuery(
ComplexQuery(
SimpleQuery(portal_type=["Support Request", "Regularisation Request"]),
SimpleQuery(simulation_state="suspended"),
SimpleQuery(destination_decision_uid=person_uid),
logical_operator='and'),
ComplexQuery(
SimpleQuery(portal_type="Upgrade Decision"),
SimpleQuery(simulation_state="confirmed"),
SimpleQuery(destination_decision_uid=person_uid),
logical_operator='and'),
logical_operator='or')
return portal.portal_catalog(query=query, **kw)
portal = context.getPortalObject()
pending_ticket_list_amount = len(context.Person_getSlapOSPendingTicket())
notification_message = portal.portal_notifications.getDocumentValue(
reference="slapos-crm-person-pending-ticket-notification")
if notification_message is not None:
mapping_dict = {'username': context.getTitle(),
'amount': pending_ticket_list_amount,
'website': portal.portal_preferences.getPreferredSlaposWebSiteUrl()}
return notification_message.getTitle(), notification_message.asText(
substitution_method_parameter_dict={'mapping_dict': mapping_dict})
message = """ You have %s pending tickets """ % pending_ticket_list_amount
return message, message
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_getSlapOSPendingTicketMessageTemplate</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
amount = len(context.Person_getSlapOSPendingTicket())
if amount > 0:
title, reminder_message = context.Person_getSlapOSPendingTicketMessageTemplate()
return context.Person_sendSlapOSPendingTicketNotification(
title,
reminder_message,
batch_mode=1
)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_sendPendingTicketReminder</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
portal = context.getPortalObject()
start_date = DateTime()
# Rely on Trade condition (like in ticket to set the proper sender)
trade_condition = portal.sale_trade_condition_module.slapos_ticket_trade_condition
event_kw = {
'portal_type' : "Mail Message",
'title' : response_event_title,
'resource' : "service_module/slapos_crm_information",
'source' : trade_condition.getSource(),
'destination' : context.getRelativeUrl(),
'start_date' : start_date,
'text_content' : response_event_text_content,
'content_type' : 'text/plain',
}
# Create event
event = portal.event_module.newContent(**event_kw)
event.start(send_mail=True, comment="Sent via Person_sendSlapOSPendingTicketNotification")
if batch_mode:
return event
message = portal.Base_translateString('New event created.')
return event.Base_redirect(keep_items={'portal_status_message': message})
<?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>response_event_title, response_event_text_content, batch_mode=False, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_sendSlapOSPendingTicketNotification</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" 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/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>Person_sendSlapOSPendingTicketNotification</string> </value>
</item>
<item>
<key> <string>action_title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list>
<string>listbox</string>
</list>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list>
<string>your_response_event_title</string>
<string>your_response_event_text_content</string>
</list>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list>
<string>listbox_delivery_start_date</string>
</list>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>my_title</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_viewSlapOSPendingTicketDialog</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>Person_viewSlapOSPendingTicket</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_dialog</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Send Pending Ticket Report</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>columns</string>
<string>default_params</string>
<string>description</string>
<string>editable</string>
<string>editable_columns</string>
<string>list_method</string>
<string>portal_types</string>
<string>selection_name</string>
<string>sort</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>listbox</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>columns</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable_columns</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>list_method</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>portal_types</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>selection_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>sort</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>columns</string> </key>
<value>
<list>
<tuple>
<string>getTitle</string>
<string>Title</string>
</tuple>
<tuple>
<string>translated_portal_type</string>
<string>Event Type</string>
</tuple>
<tuple>
<string>delivery.start_date</string>
<string>Date</string>
</tuple>
<tuple>
<string>translated_simulation_state_title</string>
<string>State</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>default_params</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>List of all Support Requests related to the follow up ticket</string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>editable_columns</string> </key>
<value>
<list>
<tuple>
<string>delivery.start_date</string>
<string>Date</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_listbox</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>list_method</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>portal_types</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>selection_name</string> </key>
<value> <string>person_pending_ticket_view_selection</string> </value>
</item>
<item>
<key> <string>sort</string> </key>
<value>
<list>
<tuple>
<string>delivery.start_date</string>
<string>asc</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Tickets</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Method" module="Products.Formulator.MethodField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>method_name</string> </key>
<value> <string>Person_getSlapOSPendingTicket</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="DateTimeField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>listbox_delivery_start_date</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>datetime_out_of_range</string> </key>
<value> <string>The date and time you entered were out of range.</string> </value>
</item>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>not_datetime</string> </key>
<value> <string>You did not enter a valid date and time.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>allow_empty_time</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>ampm_time_style</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>date_only</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>date_separator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default_now</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end_datetime</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden_day_is_last_day</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hide_day</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>input_order</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>input_style</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start_datetime</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>time_separator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>timezone_style</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>sub_form</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>allow_empty_time</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>ampm_time_style</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>date_only</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>date_separator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default_now</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end_datetime</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden_day_is_last_day</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hide_day</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>input_order</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>input_style</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start_datetime</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>time_separator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>timezone_style</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>allow_empty_time</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>ampm_time_style</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string>date_field</string> </value>
</item>
<item>
<key> <string>date_only</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>date_separator</string> </key>
<value> <string>/</string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_now</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>The Date</string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>end_datetime</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>hidden_day_is_last_day</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>hide_day</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>input_order</string> </key>
<value> <string>ymd</string> </value>
</item>
<item>
<key> <string>input_style</string> </key>
<value> <string>text</string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>start_datetime</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>time_separator</string> </key>
<value> <string>:</string> </value>
</item>
<item>
<key> <string>timezone_style</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Date</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<global name="BasicForm" module="Products.Formulator.Form"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>fields</string> </key>
<value>
<dictionary>
<item>
<key> <string>ampm</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>day</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
<item>
<key> <string>hour</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAY=</string> </persistent>
</value>
</item>
<item>
<key> <string>minute</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAc=</string> </persistent>
</value>
</item>
<item>
<key> <string>month</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAg=</string> </persistent>
</value>
</item>
<item>
<key> <string>timezone</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAk=</string> </persistent>
</value>
</item>
<item>
<key> <string>year</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAo=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>Default</string>
<string>date</string>
<string>time</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>Default</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>date</string> </key>
<value>
<list>
<string>year</string>
<string>month</string>
<string>day</string>
</list>
</value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<list>
<string>hour</string>
<string>minute</string>
<string>ampm</string>
<string>timezone</string>
</list>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>ISO-8859-1</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Basic Form</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<tuple>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>preferences/getPreferredDateOrder | string:ymd</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<tuple>
<global name="StringField" module="Products.Formulator.StandardFields"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>ampm</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
<item>
<key> <string>too_long</string> </key>
<value> <string>Too much input was given.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>truncate</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>truncate</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <int>2</int> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>2</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <int>2</int> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>am/pm</string> </value>
</item>
<item>
<key> <string>truncate</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<tuple>
<global name="IntegerField" module="Products.Formulator.StandardFields"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>day</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>integer_out_of_range</string> </key>
<value> <string>The integer you entered was out of range.</string> </value>
</item>
<item>
<key> <string>not_integer</string> </key>
<value> <string>You did not enter an integer.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <int>2</int> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>2</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Day</string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="6" aka="AAAAAAAAAAY=">
<pickle>
<tuple>
<global name="IntegerField" module="Products.Formulator.StandardFields"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>hour</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>integer_out_of_range</string> </key>
<value> <string>The integer you entered was out of range.</string> </value>
</item>
<item>
<key> <string>not_integer</string> </key>
<value> <string>You did not enter an integer.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <int>2</int> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>2</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Hour</string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="7" aka="AAAAAAAAAAc=">
<pickle>
<tuple>
<global name="IntegerField" module="Products.Formulator.StandardFields"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>minute</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>integer_out_of_range</string> </key>
<value> <string>The integer you entered was out of range.</string> </value>
</item>
<item>
<key> <string>not_integer</string> </key>
<value> <string>You did not enter an integer.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <int>2</int> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>2</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Minute</string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="8" aka="AAAAAAAAAAg=">
<pickle>
<tuple>
<global name="IntegerField" module="Products.Formulator.StandardFields"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>month</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>integer_out_of_range</string> </key>
<value> <string>The integer you entered was out of range.</string> </value>
</item>
<item>
<key> <string>not_integer</string> </key>
<value> <string>You did not enter an integer.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <int>2</int> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>2</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Month</string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="9" aka="AAAAAAAAAAk=">
<pickle>
<tuple>
<global name="ListField" module="Products.Formulator.StandardFields"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>timezone</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
<item>
<key> <string>unknown_selection</string> </key>
<value> <string>You selected an item that was not in the list.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>first_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>items</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>first_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>items</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string>GMT</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>first_item</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>items</string> </key>
<value>
<list>
<tuple>
<string>GMT-12</string>
<string>GMT-12</string>
</tuple>
<tuple>
<string>GMT-11</string>
<string>GMT-11</string>
</tuple>
<tuple>
<string>GMT-10</string>
<string>GMT-10</string>
</tuple>
<tuple>
<string>GMT-9</string>
<string>GMT-9</string>
</tuple>
<tuple>
<string>GMT-8</string>
<string>GMT-8</string>
</tuple>
<tuple>
<string>GMT-7</string>
<string>GMT-7</string>
</tuple>
<tuple>
<string>GMT-6</string>
<string>GMT-6</string>
</tuple>
<tuple>
<string>GMT-5</string>
<string>GMT-5</string>
</tuple>
<tuple>
<string>GMT-4</string>
<string>GMT-4</string>
</tuple>
<tuple>
<string>GMT-3</string>
<string>GMT-3</string>
</tuple>
<tuple>
<string>GMT-2</string>
<string>GMT-2</string>
</tuple>
<tuple>
<string>GMT-1</string>
<string>GMT-1</string>
</tuple>
<tuple>
<string>GMT</string>
<string>GMT</string>
</tuple>
<tuple>
<string>GMT+1</string>
<string>GMT+1</string>
</tuple>
<tuple>
<string>GMT+2</string>
<string>GMT+2</string>
</tuple>
<tuple>
<string>GMT+3</string>
<string>GMT+3</string>
</tuple>
<tuple>
<string>GMT+4</string>
<string>GMT+4</string>
</tuple>
<tuple>
<string>GMT+5</string>
<string>GMT+5</string>
</tuple>
<tuple>
<string>GMT+6</string>
<string>GMT+6</string>
</tuple>
<tuple>
<string>GMT+7</string>
<string>GMT+7</string>
</tuple>
<tuple>
<string>GMT+8</string>
<string>GMT+8</string>
</tuple>
<tuple>
<string>GMT+9</string>
<string>GMT+9</string>
</tuple>
<tuple>
<string>GMT+10</string>
<string>GMT+10</string>
</tuple>
<tuple>
<string>GMT+11</string>
<string>GMT+11</string>
</tuple>
<tuple>
<string>GMT+12</string>
<string>GMT+12</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Timezone</string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="10" aka="AAAAAAAAAAo=">
<pickle>
<tuple>
<global name="IntegerField" module="Products.Formulator.StandardFields"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>year</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>integer_out_of_range</string> </key>
<value> <string>The integer you entered was out of range.</string> </value>
</item>
<item>
<key> <string>not_integer</string> </key>
<value> <string>You did not enter an integer.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>end</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>start</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Year</string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>editable</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_title</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_ticket_title</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewCRMFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>default</string>
<string>editable</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_response_event_text_content</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_text_content</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewCRMFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Message</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: context.Person_getSlapOSPendingTicketMessageTemplate()[1]</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>default</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_response_event_title</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Title</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: "You have %s pending tickets to take action" % (len(context.Person_getSlapOSPendingTicket()))</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
portal = context.getPortalObject()
project = context
causality_value = portal.restrictedTraverse(causality)
if causality_value.Base_getSupportRequestInProgress(title=title) is not None:
return
destination_decision_value = portal.restrictedTraverse(destination_decision)
return destination_decision_value.Entity_createTicketFromTradeCondition(
portal.service_module.slapos_crm_monitoring.getRelativeUrl(),
title,
text_content,
source_project=project.getRelativeUrl(),
causality=causality
)
......@@ -50,11 +50,11 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>**kw</string> </value>
<value> <string>title, text_content, causality, destination_decision</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_getSlapOSPendingTicket</string> </value>
<value> <string>Project_createSupportRequestWithCausality</string> </value>
</item>
</dictionary>
</pickle>
......
from DateTime import DateTime
portal = context.getPortalObject()
open_sale_order_list = portal.portal_catalog(
portal_type="Open Sale Order",
children_portal_type="Open Sale Order Line",
validation_state="validated")
context.edit(
source=context.organisation_module.slapos,
destination=[i.getDestination() for i in open_sale_order_list])
return context.Base_redirect()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SiteMessage_setSlapOSUserSourceAndDestinatationList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
event = context.Ticket_getCausalityValue()
error_list = []
if event:
if event.getSource() != context.getDestinationDecision():
error_list.append(
'Sender of the related event should be the customer')
if event.getDestination() != context.getSourceSection():
error_list.append(
'Destination of the related event should be the slapos organisation')
return error_list
<?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>fixit=False</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SupportRequest_checkCausalitySourceDestinationConsistency</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
portal = context.getPortalObject()
return portal.portal_catalog.getResultValue(
title=title,
follow_up_uid=context.getUid(),
title={'query': title, 'key': 'ExactMatch'},
follow_up__uid=context.getUid(),
portal_type=portal.getPortalEventTypeList(),
sort_on=[('delivery.start_date', 'DESC')])
......@@ -109,6 +109,11 @@ if aggregate_portal_type == "Instance Tree":
message_list.append("%s has error (%s, %s at %s scope %s)" % (instance.getReference(), instance.getTitle(),
instance.getUrlString(), compute_node.getReference(),
compute_node.getAllocationScope()))
if instance.getPortalType() == "Software Instance" and \
compute_node.getAllocationScope() in ["closed/outdated"] and \
instance.getSlapState() == "start_requested" and \
instance.SoftwareInstance_hasReportedError():
message_list.append("%s on a %s compute_node" % (instance.getReference(), compute_node.getAllocationScope()) )
else:
message_list.append("%s is not allocated" % instance.getReference())
return ",".join(message_list)
......
""" Close Support Request which are related to a Destroy Requested Instance. """
portal = context.getPortalObject()
if context.getSimulationState() == "invalidated":
return
document = context.getAggregateValue()
if document is not None and document.getSlapState() == "destroy_requested":
person = context.getDestinationDecision(portal_type="Person")
if not person:
return
# Send Notification message
message = """ Closing this ticket as the Instance Tree was destroyed by the user.
"""
notification_message = portal.portal_notifications.getDocumentValue(
reference="slapos-crm-support-request-close-destroyed-notification")
if notification_message is not None:
mapping_dict = {'instance_tree_title': document.getTitle()}
message = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':mapping_dict})
ticket_title = "Instance Tree was destroyed was destroyed by the user"
event = context.SupportRequest_getLastEvent(ticket_title)
if event is None:
context.notify(message_title=ticket_title, message=message)
context.invalidate()
return context.REQUEST.get("ticket_notified_item")
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SupportRequest_updateMonitoringDestroyRequestedState</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
""" Close Support Request which are related to a Destroy Requested Instance. """
from Products.ERP5Type.Errors import UnsupportedWorkflowMethod
portal = context.getPortalObject()
if context.getSimulationState() == "invalidated":
return
document = context.getAggregateValue(portal_type="Instance Tree")
document = context.getCausalityValue(portal_type="Instance Tree")
if document is None:
return
return
if document.getSlapState() == "destroy_requested":
# Send Notification message
message = """ Closing this ticket as the Instance Tree was destroyed by the user.
"""
ticket_title = "Instance Tree was destroyed was destroyed by the user"
event = context.SupportRequest_getLastEvent(ticket_title)
if event is None:
context.Ticket_createProjectEvent(
ticket_title, 'outgoing', 'Web Message',
portal.service_module.slapos_crm_information.getRelativeUrl(),
text_content=message,
content_type='text/plain',
notification_message="slapos-crm-support-request-close-destroyed-notification",
#language=XXX,
substitution_method_parameter_dict={
'instance_tree_title': document.getTitle()
}
)
if document.getPortalType() == "Instance Tree":
if document.getSlapState() == "destroy_requested":
return context.SupportRequest_updateMonitoringDestroyRequestedState()
try:
context.validate()
except UnsupportedWorkflowMethod:
pass
context.invalidate()
return event
"""Generic script to add event
It creates new Event for any context which become follow_up of created Event.
"""
portal = context.getPortalObject()
ticket = context
if direction == 'outgoing':
source_relative_url = source or ticket.getSource()
source_section_relative_url = ticket.getSourceSection()
source_project_relative_url = ticket.getSourceProject()
destination_relative_url = destination or ticket.getDestination()
destination_section_relative_url = ticket.getDestinationSection()
destination_project_relative_url = ticket.getDestinationProject()
elif direction == 'incoming':
source_relative_url = source or ticket.getDestination()
source_section_relative_url = ticket.getDestinationSection()
source_project_relative_url = ticket.getDestinationProject()
destination_relative_url = destination or ticket.getSource()
destination_section_relative_url = ticket.getSourceSection()
destination_project_relative_url = ticket.getSourceProject()
else:
raise NotImplementedError('The specified direction is not handled: %r' % (direction,))
event_kw = {
'portal_type': portal_type,
'resource': resource,
'source': source_relative_url,
'source_section': source_section_relative_url,
'source_project': source_project_relative_url,
'destination': destination_relative_url,
'destination_section': destination_section_relative_url,
'destination_project': destination_project_relative_url,
'start_date': DateTime(),
'follow_up_value': ticket,
'language': language,
'text_content': text_content,
'content_type': content_type,
}
# Create event
module = portal.getDefaultModule(portal_type=portal_type)
event = module.newContent(**event_kw)
if notification_message:
event.Event_setTextContentFromNotificationMessage(
notification_message,
language=language,
substitution_method_parameter_dict=substitution_method_parameter_dict
)
# Prefer using the notification message title
# as it will be correctly translated
if not event.hasTitle():
event.edit(title=title)
if not keep_draft:
if direction == 'incoming':
# Support event_workflow and event_simulation_workflow
if portal.portal_workflow.isTransitionPossible(event, 'receive'):
event.receive(comment=comment)
if portal.portal_workflow.isTransitionPossible(event, 'stop'):
event.stop(comment=comment)
else:
event.plan(comment=comment)
event.start(comment=comment, send_mail=(portal_type == 'Mail Message'))
event.stop(comment=comment)
event.deliver(comment=comment)
return event
......@@ -50,11 +50,11 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
<value> <string>title, direction, portal_type, resource, text_content, content_type, notification_message=None, substitution_method_parameter_dict=None, keep_draft=False, source=None, destination=None, language=None, comment=None, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_getSubscriptionRequestFirstUnpaidInvoiceList</string> </value>
<value> <string>Ticket_createProjectEvent</string> </value>
</item>
</dictionary>
</pickle>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -26,192 +26,15 @@ from erp5.component.test.SlapOSTestCaseMixin import \
SlapOSTestCaseMixin,SlapOSTestCaseMixinWithAbort, simulate
from zExceptions import Unauthorized
from DateTime import DateTime
import difflib
class TestSlapOSPerson_checkToCreateRegularisationRequest(SlapOSTestCaseMixinWithAbort):
@simulate('NotificationTool_getDocumentValue',
'reference=None, language="en"',
'assert reference == "slapos-crm.create.regularisation.request"\n' \
'return')
@simulate('Entity_statOutstandingAmount', '*args, **kwargs', 'return "1"')
def test_addRegularisationRequest_payment_requested(self):
for preference in \
self.portal.portal_catalog(portal_type="System Preference"):
preference = preference.getObject()
if preference.getPreferenceState() == 'global':
preference.setPreferredSlaposWebSiteUrl('http://foobar.org/')
person = self.makePerson(index=0, user=0)
before_date = DateTime()
ticket, event = person.Person_checkToCreateRegularisationRequest()
after_date = DateTime()
self.assertEqual(ticket.getPortalType(), 'Regularisation Request')
self.assertEqual(ticket.getSimulationState(), 'suspended')
self.assertEqual(ticket.getSourceProject(), person.getRelativeUrl())
self.assertEqual(ticket.getResource(),
'service_module/slapos_crm_acknowledgement')
self.assertEqual(ticket.getTitle(),
'Account regularisation expected for "%s"' % person.getTitle())
self.assertEqual(ticket.getDestination(),
person.getRelativeUrl())
self.assertEqual(ticket.getDestinationDecision(),
person.getRelativeUrl())
self.assertEqual(event.getPortalType(), 'Mail Message')
self.assertEqual(event.getResource(),
'service_module/slapos_crm_acknowledgement')
self.assertTrue(event.getStartDate() >= before_date)
self.assertTrue(event.getStopDate() <= after_date)
self.assertEqual(event.getTitle(), "Invoice payment requested")
self.assertEqual(event.getDestination(),
person.getRelativeUrl())
self.assertEqual(event.getSource(),
ticket.getSource())
expected_text_content = """Dear Member Template,
A new invoice has been generated.
You can access it in your invoice section at http://foobar.org/.
Regards,
The slapos team
"""
self.assertEqual(event.getTextContent(), expected_text_content,
'\n'.join([x for x in difflib.unified_diff(
event.getTextContent().splitlines(),
expected_text_content.splitlines())]))
self.assertEqual(event.getSimulationState(), 'delivered')
@simulate('NotificationTool_getDocumentValue',
'reference=None, language="en"',
'assert reference == "slapos-crm.create.regularisation.request"\n' \
'return context.restrictedTraverse(' \
'context.REQUEST["test_addRegularisationRequest_notification_message"])')
@simulate('Entity_statOutstandingAmount', '*args, **kwargs', 'return "1"')
def test_addRegularisationRequest_notification_message(self):
for preference in \
self.portal.portal_catalog(portal_type="System Preference"):
preference = preference.getObject()
if preference.getPreferenceState() == 'global':
preference.setPreferredSlaposWebSiteUrl('http://foobar.org/')
person = self.makePerson(index=0, user=0)
new_id = self.generateNewId()
notification_message = self.portal.notification_message_module.newContent(
portal_type="Notification Message",
title='Test NM title %s' % new_id,
text_content='Test NM content\n%s\n' % new_id,
content_type='text/plain',
)
self.portal.REQUEST\
['test_addRegularisationRequest_notification_message'] = \
notification_message.getRelativeUrl()
before_date = DateTime()
ticket, event = person.Person_checkToCreateRegularisationRequest()
after_date = DateTime()
self.assertEqual(ticket.getPortalType(), 'Regularisation Request')
self.assertEqual(ticket.getSimulationState(), 'suspended')
self.assertEqual(ticket.getSourceProject(), person.getRelativeUrl())
self.assertEqual(ticket.getResource(),
'service_module/slapos_crm_acknowledgement')
self.assertEqual(ticket.getTitle(),
'Account regularisation expected for "%s"' % person.getTitle())
self.assertEqual(ticket.getDestination(),
person.getRelativeUrl())
self.assertEqual(ticket.getDestinationDecision(),
person.getRelativeUrl())
self.assertEqual(event.getPortalType(), 'Mail Message')
self.assertEqual(event.getResource(),
'service_module/slapos_crm_acknowledgement')
self.assertTrue(event.getStartDate() >= before_date)
self.assertTrue(event.getStopDate() <= after_date)
self.assertEqual(event.getTitle(),
'Test NM title %s' % new_id)
self.assertEqual(event.getDestination(),
person.getRelativeUrl())
self.assertEqual(event.getSource(),
ticket.getSource())
expected_text_content = 'Test NM content\n%s\n' % new_id
self.assertEqual(event.getTextContent(), expected_text_content,
'\n'.join([x for x in difflib.unified_diff(
event.getTextContent().splitlines(),
expected_text_content.splitlines())]))
self.assertEqual(event.getSimulationState(), 'delivered')
# def test_addRegularisationRequest_do_not_duplicate_ticket(self):
# person = self.createPerson()
# ticket = person.Person_checkToCreateRegularisationRequest()
# ticket2 = person.Person_checkToCreateRegularisationRequest()
# self.assertEqual(ticket.getRelativeUrl(), ticket2.getRelativeUrl())
@simulate('Entity_statOutstandingAmount', '*args, **kwargs', 'return "1"')
def test_addRegularisationRequest_do_not_duplicate_ticket_if_not_reindexed(self):
person = self.makePerson(index=0, user=0)
ticket, event = person.Person_checkToCreateRegularisationRequest()
transaction.commit()
ticket2, event2 = person.Person_checkToCreateRegularisationRequest()
self.assertNotEqual(ticket, None)
self.assertNotEqual(event, None)
self.assertEqual(ticket2, None)
self.assertEqual(event2, None)
@simulate('Entity_statOutstandingAmount', '*args, **kwargs', 'return "0"')
@simulate('RegularisationRequest_checkToSendUniqEvent',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_addRegularisationRequest_balance_ok(self):
person = self.makePerson(index=0, user=0)
ticket, event = person.Person_checkToCreateRegularisationRequest()
self.assertEqual(ticket, None)
self.assertEqual(event, None)
@simulate('Entity_statOutstandingAmount', '*args, **kwargs', 'return "1"')
def test_addRegularisationRequest_existing_suspended_ticket(self):
person = self.makePerson(index=0, user=0)
ticket, event = person.Person_checkToCreateRegularisationRequest()
transaction.commit()
self.tic()
ticket2, event2 = person.Person_checkToCreateRegularisationRequest()
self.assertNotEqual(ticket, None)
self.assertNotEqual(event, None)
self.assertEqual(ticket2.getRelativeUrl(), ticket.getRelativeUrl())
self.assertEqual(event2, None)
@simulate('Entity_statOutstandingAmount', '*args, **kwargs', 'return "1"')
def test_addRegularisationRequest_existing_validated_ticket(self):
person = self.makePerson(index=0, user=0)
ticket, event = person.Person_checkToCreateRegularisationRequest()
ticket.validate()
transaction.commit()
self.tic()
ticket2, event2 = person.Person_checkToCreateRegularisationRequest()
self.assertNotEqual(ticket, None)
self.assertNotEqual(event, None)
self.assertEqual(ticket2.getRelativeUrl(), ticket.getRelativeUrl())
self.assertEqual(event2, None)
@simulate('Entity_statOutstandingAmount', '*args, **kwargs', 'return "1"')
def test_addRegularisationRequest_existing_invalidated_ticket(self):
person = self.makePerson(index=0, user=0)
ticket = person.Person_checkToCreateRegularisationRequest()[0]
ticket.invalidate()
transaction.commit()
self.tic()
ticket2, event2 = person.Person_checkToCreateRegularisationRequest()
self.assertNotEqual(ticket2.getRelativeUrl(), ticket.getRelativeUrl())
self.assertNotEqual(event2, None)
def test_addRegularisationRequest_REQUEST_disallowed(self):
person = self.makePerson(index=0, user=0)
self.assertRaises(
Unauthorized,
person.Person_checkToCreateRegularisationRequest,
REQUEST={})
class TestSlapOSRegularisationRequest_invalidateIfPersonBalanceIsOk(
SlapOSTestCaseMixinWithAbort):
class TestSlapOSRegularisationRequest_checkToSendUniqEvent(SlapOSTestCaseMixin):
def afterSetUp(self):
SlapOSTestCaseMixin.afterSetUp(self)
expected_slapos_organisation = self.portal.organisation_module.newContent(
default_email_coordinate_text="foo@example.org"
)
self.expected_slapos_organisation = expected_slapos_organisation.getRelativeUrl()
def createRegularisationRequest(self):
new_id = self.generateNewId()
......@@ -219,65 +42,42 @@ class TestSlapOSRegularisationRequest_invalidateIfPersonBalanceIsOk(
portal_type='Regularisation Request',
title="Test Reg. Req.%s" % new_id,
reference="TESTREGREQ-%s" % new_id,
resource='foo/bar',
)
def test_invalidateIfPersonBalanceIsOk_REQUEST_disallowed(self):
ticket = self.createRegularisationRequest()
self.assertRaises(
Unauthorized,
ticket.RegularisationRequest_invalidateIfPersonBalanceIsOk,
REQUEST={})
@simulate('Entity_statOutstandingAmount', '*args, **kwargs', 'return "0"')
def test_invalidateIfPersonBalanceIsOk_matching_case(self):
person = self.makePerson(index=0, user=0)
ticket = self.createRegularisationRequest()
ticket.edit(source_project_value=person)
ticket.validate()
ticket.suspend()
ticket.RegularisationRequest_invalidateIfPersonBalanceIsOk()
self.assertEqual(ticket.getSimulationState(), 'invalidated')
@simulate('Entity_statOutstandingAmount', '*args, **kwargs', 'return "0"')
def test_invalidateIfPersonBalanceIsOk_validated(self):
person = self.makePerson(index=0, user=0)
ticket = self.createRegularisationRequest()
ticket.edit(source_project_value=person)
ticket.validate()
ticket.RegularisationRequest_invalidateIfPersonBalanceIsOk()
self.assertEqual(ticket.getSimulationState(), 'invalidated')
@simulate('Entity_statOutstandingAmount', '*args, **kwargs', 'return "0"')
def test_invalidateIfPersonBalanceIsOk_no_person(self):
def test_checkToSendUniqEvent_noEvent(self):
project = self.addProject()
person = self.makePerson(project, index=0, user=0)
ticket = self.createRegularisationRequest()
ticket.edit(
source=self.expected_slapos_organisation,
destination_value=person,
source_project_value=person)
ticket.validate()
ticket.suspend()
ticket.RegularisationRequest_invalidateIfPersonBalanceIsOk()
self.assertEqual(ticket.getSimulationState(), 'suspended')
before_date = DateTime()
event = ticket.RegularisationRequest_checkToSendUniqEvent(
'service_module/slapos_crm_spam', 'foo title', 'foo content', 'foo comment')
after_date = DateTime()
@simulate('Entity_statOutstandingAmount', '*args, **kwargs', 'return "1"')
def test_invalidateIfPersonBalanceIsOk_wrong_balance(self):
person = self.makePerson(index=0, user=0)
ticket = self.createRegularisationRequest()
ticket.edit(source_project_value=person)
ticket.validate()
ticket.suspend()
ticket.RegularisationRequest_invalidateIfPersonBalanceIsOk()
self.assertEqual(ticket.getSimulationState(), 'suspended')
self.assertEqual(ticket.getResource(), 'service_module/slapos_crm_spam')
class TestSlapOSRegularisationRequest_checkToSendUniqEvent(SlapOSTestCaseMixin):
def createRegularisationRequest(self):
new_id = self.generateNewId()
return self.portal.regularisation_request_module.newContent(
portal_type='Regularisation Request',
title="Test Reg. Req.%s" % new_id,
reference="TESTREGREQ-%s" % new_id,
resource='foo/bar',
)
self.assertEqual(event.getPortalType(), 'Mail Message')
self.assertEqual(event.getSimulationState(), 'delivered')
self.assertTrue(event.getStartDate() >= before_date)
self.assertTrue(event.getStopDate() <= after_date)
self.assertEqual(event.getTitle(), "foo title")
self.assertEqual(event.getResource(), 'service_module/slapos_crm_spam')
self.assertEqual(event.getFollowUp(), ticket.getRelativeUrl())
self.assertEqual(event.getSource(), self.expected_slapos_organisation)
self.assertEqual(event.getDestination(), person.getRelativeUrl())
self.assertEqual(event.getTextContent(), 'foo content')
self.assertEqual(event.getContentType(), 'text/plain')
def test_checkToSendUniqEvent_no_event(self):
person = self.makePerson(index=0, user=0)
def test_checkToSendUniqEvent_notificationMessage(self):
project = self.addProject()
person = self.makePerson(project, index=0, user=0)
ticket = self.createRegularisationRequest()
ticket.edit(
source=self.expected_slapos_organisation,
......@@ -286,8 +86,25 @@ class TestSlapOSRegularisationRequest_checkToSendUniqEvent(SlapOSTestCaseMixin):
ticket.validate()
ticket.suspend()
before_date = DateTime()
notification_message_reference = 'test_checkToSendUniqEvent_notificationMessage%s' % self.generateNewId()
notification_message = self.portal.notification_message_module.newContent(
portal_type="Notification Message",
reference=notification_message_reference,
title='notification title ${foo}',
text_content_substitution_mapping_method_id='NotificationMessage_getSubstitutionMappingDictFromEvent',
text_content='notification content ${foo}',
content_type='text/html',
)
notification_message.validate()
self.tic()
event = ticket.RegularisationRequest_checkToSendUniqEvent(
'service_module/slapos_crm_spam', 'foo title', 'foo content', 'foo comment')
'service_module/slapos_crm_spam', 'foo title', 'foo content',
'foo comment',
notification_message=notification_message_reference,
substitution_method_parameter_dict={'foo': 'bar'}
)
after_date = DateTime()
self.assertEqual(ticket.getSimulationState(), 'suspended')
......@@ -297,14 +114,15 @@ class TestSlapOSRegularisationRequest_checkToSendUniqEvent(SlapOSTestCaseMixin):
self.assertEqual(event.getSimulationState(), 'delivered')
self.assertTrue(event.getStartDate() >= before_date)
self.assertTrue(event.getStopDate() <= after_date)
self.assertEqual(event.getTitle(), "foo title")
self.assertEqual(event.getTitle(), "notification title bar")
self.assertEqual(event.getResource(), 'service_module/slapos_crm_spam')
self.assertEqual(event.getFollowUp(), ticket.getRelativeUrl())
self.assertEqual(event.getSource(), self.expected_slapos_organisation)
self.assertEqual(event.getDestination(), person.getRelativeUrl())
self.assertEqual(event.getTextContent(), 'foo content')
self.assertEqual(event.getTextContent(), 'notification content bar')
self.assertEqual(event.getContentType(), 'text/html')
def test_checkToSendUniqEvent_service_required(self):
def test_checkToSendUniqEvent_serviceRequired(self):
ticket = self.createRegularisationRequest()
self.assertRaises(
AssertionError,
......@@ -312,8 +130,9 @@ class TestSlapOSRegularisationRequest_checkToSendUniqEvent(SlapOSTestCaseMixin):
ticket.getRelativeUrl(), '', '', ''
)
def test_checkToSendUniqEvent_call_twice_with_tic(self):
person = self.makePerson(index=0, user=0)
def test_checkToSendUniqEvent_callTwiceWithTic(self):
project = self.addProject()
person = self.makePerson(project, index=0, user=0)
ticket = self.createRegularisationRequest()
ticket.edit(
source=self.expected_slapos_organisation,
......@@ -331,8 +150,9 @@ class TestSlapOSRegularisationRequest_checkToSendUniqEvent(SlapOSTestCaseMixin):
self.assertEqual(event.getTextContent(), 'foo content')
self.assertEqual(event.getRelativeUrl(), event2.getRelativeUrl())
def test_checkToSendUniqEvent_manual_event(self):
person = self.makePerson(index=0, user=0)
def test_checkToSendUniqEvent_manualEvent(self):
project = self.addProject()
person = self.makePerson(project, index=0, user=0)
ticket = self.createRegularisationRequest()
ticket.edit(
source=self.expected_slapos_organisation,
......@@ -356,8 +176,9 @@ class TestSlapOSRegularisationRequest_checkToSendUniqEvent(SlapOSTestCaseMixin):
self.assertEqual(event.getSimulationState(), 'draft')
self.assertEqual(event.getRelativeUrl(), event2.getRelativeUrl())
def test_checkToSendUniqEvent_not_suspended(self):
person = self.makePerson(index=0, user=0)
def test_checkToSendUniqEvent_notSuspended(self):
project = self.addProject()
person = self.makePerson(project, index=0, user=0)
ticket = self.createRegularisationRequest()
ticket.edit(
source=self.expected_slapos_organisation,
......@@ -369,8 +190,9 @@ class TestSlapOSRegularisationRequest_checkToSendUniqEvent(SlapOSTestCaseMixin):
'service_module/slapos_crm_spam', 'foo2 title', 'foo2 content', 'foo2 comment')
self.assertEqual(event, None)
def test_checkToSendUniqEvent_event_not_reindexed(self):
person = self.makePerson(index=0, user=0)
def test_checkToSendUniqEvent_eventNotReindexed(self):
project = self.addProject()
person = self.makePerson(project, index=0, user=0)
ticket = self.createRegularisationRequest()
ticket.edit(
source=self.expected_slapos_organisation,
......@@ -386,7 +208,7 @@ class TestSlapOSRegularisationRequest_checkToSendUniqEvent(SlapOSTestCaseMixin):
self.assertNotEqual(event, event2)
self.assertEqual(event2, None)
def test_checkToSendUniqEvent_REQUEST_disallowed(self):
def test_checkToSendUniqEvent_REQUESTdisallowed(self):
ticket = self.createRegularisationRequest()
self.assertRaises(
Unauthorized,
......@@ -394,6 +216,7 @@ class TestSlapOSRegularisationRequest_checkToSendUniqEvent(SlapOSTestCaseMixin):
'', '', '', '',
REQUEST={})
class TestSlapOSRegularisationRequest_checkToTriggerNextEscalationStep(
SlapOSTestCaseMixinWithAbort):
......@@ -414,18 +237,16 @@ class TestSlapOSRegularisationRequest_checkToTriggerNextEscalationStep(
0, ticket.getRelativeUrl(), '', '', '', ''
)
@simulate('NotificationTool_getDocumentValue',
'reference=None, language="en"',
'assert reference == "slapos-crm.acknowledgment.escalation", reference\n' \
'return')
@simulate('RegularisationRequest_checkToSendUniqEvent',
'service_relative_url, title, text_content, comment, REQUEST=None',
'service_relative_url, title, text_content, comment, ' \
'notification_message=None, substitution_method_parameter_dict=None, ' \
'REQUEST=None',
'context.portal_workflow.doActionFor(' \
'context, action="edit_action", ' \
'comment="Visited by RegularisationRequest_checkToSendUniqEvent ' \
'%s %s %s %s" % (service_relative_url, title, text_content, comment))\n' \
'%s %s %s %s %s %s" % (service_relative_url, title, text_content, comment, notification_message, substitution_method_parameter_dict))\n' \
'return "fooevent"')
def test_checkToTriggerNextEscalationStep_matching_event(self):
def test_checkToTriggerNextEscalationStep_matchingEvent(self):
ticket = self.createRegularisationRequest()
ticket.edit(resource='service_module/slapos_crm_acknowledgement')
ticket.validate()
......@@ -442,19 +263,21 @@ class TestSlapOSRegularisationRequest_checkToTriggerNextEscalationStep(
event2 = ticket.RegularisationRequest_checkToTriggerNextEscalationStep(
7, 'service_module/slapos_crm_acknowledgement',
'service_module/slapos_crm_spam',
'foo2 title', 'foo2 content', 'foo2 comment')
'foo2 title', 'foo2 content', 'foo2 comment',
notification_message='slapos-crm.acknowledgment.escalation',
substitution_method_parameter_dict={'foo': 'bar'})
self.assertEqual(event2, event.getRelativeUrl())
self.assertEqual(
'Visited by RegularisationRequest_checkToSendUniqEvent %s %s %s %s' % \
'Visited by RegularisationRequest_checkToSendUniqEvent %s %s %s %s %s %s' % \
('service_module/slapos_crm_spam', 'foo2 title', 'foo2 content',
'foo2 comment'),
'foo2 comment', 'slapos-crm.acknowledgment.escalation', {'foo': 'bar'}),
ticket.workflow_history['edit_workflow'][-1]['comment'])
@simulate('RegularisationRequest_checkToSendUniqEvent',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_checkToTriggerNextEscalationStep_recent_event(self):
def test_checkToTriggerNextEscalationStep_recentEvent(self):
ticket = self.createRegularisationRequest()
ticket.edit(resource='service_module/slapos_crm_acknowledgement')
ticket.validate()
......@@ -478,7 +301,7 @@ class TestSlapOSRegularisationRequest_checkToTriggerNextEscalationStep(
@simulate('RegularisationRequest_checkToSendUniqEvent',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_checkToTriggerNextEscalationStep_other_ticket_event(self):
def test_checkToTriggerNextEscalationStep_other_ticketEvent(self):
ticket = self.createRegularisationRequest()
ticket.edit(resource='service_module/slapos_crm_acknowledgement')
ticket.validate()
......@@ -501,7 +324,7 @@ class TestSlapOSRegularisationRequest_checkToTriggerNextEscalationStep(
@simulate('RegularisationRequest_checkToSendUniqEvent',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_checkToTriggerNextEscalationStep_other_resource_event(self):
def test_checkToTriggerNextEscalationStep_otherResourceEvent(self):
ticket = self.createRegularisationRequest()
ticket.edit(resource='service_module/slapos_crm_acknowledgement')
ticket.validate()
......@@ -525,7 +348,7 @@ class TestSlapOSRegularisationRequest_checkToTriggerNextEscalationStep(
@simulate('RegularisationRequest_checkToSendUniqEvent',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_checkToTriggerNextEscalationStep_no_current_event(self):
def test_checkToTriggerNextEscalationStep_noCurrentEvent(self):
ticket = self.createRegularisationRequest()
ticket.edit(resource='service_module/slapos_crm_acknowledgement')
ticket.validate()
......@@ -542,7 +365,7 @@ class TestSlapOSRegularisationRequest_checkToTriggerNextEscalationStep(
@simulate('RegularisationRequest_checkToSendUniqEvent',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_checkToTriggerNextEscalationStep_no_ticket_resource(self):
def test_checkToTriggerNextEscalationStep_noTicketResource(self):
ticket = self.createRegularisationRequest()
ticket.validate()
ticket.suspend()
......@@ -565,7 +388,7 @@ class TestSlapOSRegularisationRequest_checkToTriggerNextEscalationStep(
@simulate('RegularisationRequest_checkToSendUniqEvent',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_checkToTriggerNextEscalationStep_not_suspended(self):
def test_checkToTriggerNextEscalationStep_notSuspended(self):
ticket = self.createRegularisationRequest()
ticket.edit(resource='service_module/slapos_crm_acknowledgement')
ticket.validate()
......@@ -585,7 +408,7 @@ class TestSlapOSRegularisationRequest_checkToTriggerNextEscalationStep(
self.assertEqual(event2, None)
def test_checkToTriggerNextEscalationStep_REQUEST_disallowed(self):
def test_checkToTriggerNextEscalationStep_REQUESTdisallowed(self):
ticket = self.createRegularisationRequest()
self.assertRaises(
Unauthorized,
......@@ -593,510 +416,40 @@ class TestSlapOSRegularisationRequest_checkToTriggerNextEscalationStep(
'', '', '', '', '', '',
REQUEST={})
class TestSlapOSRegularisationRequest_triggerAcknowledgmentEscalation(
SlapOSTestCaseMixinWithAbort):
def test_triggerAcknowledgmentEscalation_REQUEST_disallowed(self):
ticket = self.createRegularisationRequest()
self.assertRaises(
Unauthorized,
ticket.RegularisationRequest_triggerAcknowledgmentEscalation,
REQUEST={})
@simulate('NotificationTool_getDocumentValue',
'reference=None, language="en"',
'assert reference == "slapos-crm.acknowledgment.escalation", reference\n' \
'return')
@simulate('RegularisationRequest_checkToTriggerNextEscalationStep',
'delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment, REQUEST=None',
'context.portal_workflow.doActionFor(' \
'context, action="edit_action", ' \
'comment="Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s" % (delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment))')
def test_checkToTriggerNextEscalationStep_matching_event(self):
ticket = self.createRegularisationRequest()
ticket.RegularisationRequest_triggerAcknowledgmentEscalation()
self.assertEqual(
'Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s' % \
(15,
'service_module/slapos_crm_acknowledgement',
'service_module/slapos_crm_stop_reminder',
'Reminder: invoice payment requested',
"""Dear user,
We would like to remind you the unpaid invoice you have on %s.
If no payment is done during the coming days, we will stop all your current instances to free some hardware resources.
Regards,
The slapos team
""" % self.portal.portal_preferences.getPreferredSlaposWebSiteUrl(),
'Stopping reminder.'),
ticket.workflow_history['edit_workflow'][-1]['comment'])
@simulate('NotificationTool_getDocumentValue',
'reference=None, language="en"',
'assert reference == "slapos-crm.acknowledgment.escalation"\n' \
'return context.restrictedTraverse(' \
'context.REQUEST["test_checkToTriggerNextEscalationStep_notification_message"])')
@simulate('RegularisationRequest_checkToTriggerNextEscalationStep',
'delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment, REQUEST=None',
'context.portal_workflow.doActionFor(' \
'context, action="edit_action", ' \
'comment="Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s" % (delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment))')
def test_checkToTriggerNextEscalationStep_notification_message(self):
ticket = self.createRegularisationRequest()
new_id = self.generateNewId()
notification_message = self.portal.notification_message_module.newContent(
portal_type="Notification Message",
title='Test NM title %s' % new_id,
text_content='Test NM content\n%s\n' % new_id,
content_type='text/plain',
)
self.portal.REQUEST\
['test_checkToTriggerNextEscalationStep_notification_message'] = \
notification_message.getRelativeUrl()
ticket.RegularisationRequest_triggerAcknowledgmentEscalation()
self.assertEqual(
'Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s' % \
(15,
'service_module/slapos_crm_acknowledgement',
'service_module/slapos_crm_stop_reminder',
'Test NM title %s' % new_id,
'Test NM content\n%s\n' % new_id,
'Stopping reminder.'),
ticket.workflow_history['edit_workflow'][-1]['comment'])
class TestSlapOSRegularisationRequest_triggerStopReminderEscalation(
SlapOSTestCaseMixinWithAbort):
def test_triggerStopReminderEscalation_REQUEST_disallowed(self):
ticket = self.createRegularisationRequest()
self.assertRaises(
Unauthorized,
ticket.RegularisationRequest_triggerStopReminderEscalation,
REQUEST={})
@simulate('NotificationTool_getDocumentValue',
'reference=None, language="en"',
'assert reference == "slapos-crm.stop.reminder.escalation", reference\n' \
'return')
@simulate('RegularisationRequest_checkToTriggerNextEscalationStep',
'delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment, REQUEST=None',
'context.portal_workflow.doActionFor(' \
'context, action="edit_action", ' \
'comment="Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s" % (delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment))')
def test_checkToTriggerNextEscalationStep_matching_event(self):
ticket = self.createRegularisationRequest()
ticket.RegularisationRequest_triggerStopReminderEscalation()
self.assertEqual(
'Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s' % \
(7,
'service_module/slapos_crm_stop_reminder',
'service_module/slapos_crm_stop_acknowledgement',
'Acknowledgment: instances stopped',
"""Dear user,
Despite our last reminder, you still have an unpaid invoice on %s.
We will now stop all your current instances to free some hardware resources.
Regards,
The slapos team
""" % self.portal.portal_preferences.getPreferredSlaposWebSiteUrl(),
'Stopping acknowledgment.'),
ticket.workflow_history['edit_workflow'][-1]['comment'])
@simulate('NotificationTool_getDocumentValue',
'reference=None, language="en"',
'assert reference == "slapos-crm.stop.reminder.escalation"\n' \
'return context.restrictedTraverse(' \
'context.REQUEST["test_checkToTriggerNextEscalationStep_notification_message"])')
@simulate('RegularisationRequest_checkToTriggerNextEscalationStep',
'delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment, REQUEST=None',
'context.portal_workflow.doActionFor(' \
'context, action="edit_action", ' \
'comment="Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s" % (delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment))')
def test_checkToTriggerNextEscalationStep_notification_message(self):
ticket = self.createRegularisationRequest()
new_id = self.generateNewId()
notification_message = self.portal.notification_message_module.newContent(
portal_type="Notification Message",
title='Test NM title %s' % new_id,
text_content='Test NM content\n%s\n' % new_id,
content_type='text/plain',
)
self.portal.REQUEST\
['test_checkToTriggerNextEscalationStep_notification_message'] = \
notification_message.getRelativeUrl()
ticket.RegularisationRequest_triggerStopReminderEscalation()
self.assertEqual(
'Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s' % \
(7,
'service_module/slapos_crm_stop_reminder',
'service_module/slapos_crm_stop_acknowledgement',
'Test NM title %s' % new_id,
'Test NM content\n%s\n' % new_id,
'Stopping acknowledgment.'),
ticket.workflow_history['edit_workflow'][-1]['comment'])
class TestSlapOSRegularisationRequest_triggerStopAcknowledgmentEscalation(
SlapOSTestCaseMixinWithAbort):
def test_triggerStopAcknowledgmentEscalation_REQUEST_disallowed(self):
ticket = self.createRegularisationRequest()
self.assertRaises(
Unauthorized,
ticket.RegularisationRequest_triggerStopAcknowledgmentEscalation,
REQUEST={})
@simulate('NotificationTool_getDocumentValue',
'reference=None, language="en"',
'assert reference == "slapos-crm.stop.acknowledgment.escalation", reference\n' \
'return')
@simulate('RegularisationRequest_checkToTriggerNextEscalationStep',
'delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment, REQUEST=None',
'context.portal_workflow.doActionFor(' \
'context, action="edit_action", ' \
'comment="Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s" % (delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment))')
def test_checkToTriggerNextEscalationStep_matching_event(self):
ticket = self.createRegularisationRequest()
ticket.RegularisationRequest_triggerStopAcknowledgmentEscalation()
self.assertEqual(
'Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s' % \
(7,
'service_module/slapos_crm_stop_acknowledgement',
'service_module/slapos_crm_delete_reminder',
'Last reminder: invoice payment requested',
"""Dear user,
We would like to remind you the unpaid invoice you have on %s.
If no payment is done during the coming days, we will delete all your instances.
Regards,
The slapos team
""" % self.portal.portal_preferences.getPreferredSlaposWebSiteUrl(),
'Deleting reminder.'),
ticket.workflow_history['edit_workflow'][-1]['comment'])
@simulate('NotificationTool_getDocumentValue',
'reference=None, language="en"',
'assert reference == "slapos-crm.stop.acknowledgment.escalation"\n' \
'return context.restrictedTraverse(' \
'context.REQUEST["test_checkToTriggerNextEscalationStep_notification_message"])')
@simulate('RegularisationRequest_checkToTriggerNextEscalationStep',
'delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment, REQUEST=None',
'context.portal_workflow.doActionFor(' \
'context, action="edit_action", ' \
'comment="Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s" % (delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment))')
def test_checkToTriggerNextEscalationStep_notification_message(self):
ticket = self.createRegularisationRequest()
new_id = self.generateNewId()
notification_message = self.portal.notification_message_module.newContent(
portal_type="Notification Message",
title='Test NM title %s' % new_id,
text_content='Test NM content\n%s\n' % new_id,
content_type='text/plain',
)
self.portal.REQUEST\
['test_checkToTriggerNextEscalationStep_notification_message'] = \
notification_message.getRelativeUrl()
ticket.RegularisationRequest_triggerStopAcknowledgmentEscalation()
self.assertEqual(
'Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s' % \
(7,
'service_module/slapos_crm_stop_acknowledgement',
'service_module/slapos_crm_delete_reminder',
'Test NM title %s' % new_id,
'Test NM content\n%s\n' % new_id,
'Deleting reminder.'),
ticket.workflow_history['edit_workflow'][-1]['comment'])
class TestSlapOSRegularisationRequest_triggerDeleteReminderEscalation(
SlapOSTestCaseMixinWithAbort):
def test_triggerDeleteReminderEscalation_REQUEST_disallowed(self):
ticket = self.createRegularisationRequest()
self.assertRaises(
Unauthorized,
ticket.RegularisationRequest_triggerDeleteReminderEscalation,
REQUEST={})
@simulate('NotificationTool_getDocumentValue',
'reference=None, language="en"',
'assert reference == "slapos-crm.delete.reminder.escalation", reference\n' \
'return')
@simulate('RegularisationRequest_checkToTriggerNextEscalationStep',
'delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment, REQUEST=None',
'context.portal_workflow.doActionFor(' \
'context, action="edit_action", ' \
'comment="Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s" % (delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment))')
def test_checkToTriggerNextEscalationStep_matching_event(self):
ticket = self.createRegularisationRequest()
ticket.RegularisationRequest_triggerDeleteReminderEscalation()
self.assertEqual(
'Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s' % \
(10,
'service_module/slapos_crm_delete_reminder',
'service_module/slapos_crm_delete_acknowledgement',
'Acknowledgment: instances deleted',
"""Dear user,
Despite our last reminder, you still have an unpaid invoice on %s.
We will now delete all your instances.
Regards,
The slapos team
""" % self.portal.portal_preferences.getPreferredSlaposWebSiteUrl(),
'Deleting acknowledgment.'),
ticket.workflow_history['edit_workflow'][-1]['comment'])
@simulate('NotificationTool_getDocumentValue',
'reference=None, language="en"',
'assert reference == "slapos-crm.delete.reminder.escalation"\n' \
'return context.restrictedTraverse(' \
'context.REQUEST["test_checkToTriggerNextEscalationStep_notification_message"])')
@simulate('RegularisationRequest_checkToTriggerNextEscalationStep',
'delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment, REQUEST=None',
'context.portal_workflow.doActionFor(' \
'context, action="edit_action", ' \
'comment="Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s" % (delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment))')
def test_checkToTriggerNextEscalationStep_notification_message(self):
ticket = self.createRegularisationRequest()
new_id = self.generateNewId()
notification_message = self.portal.notification_message_module.newContent(
portal_type="Notification Message",
title='Test NM title %s' % new_id,
text_content='Test NM content\n%s\n' % new_id,
content_type='text/plain',
)
self.portal.REQUEST\
['test_checkToTriggerNextEscalationStep_notification_message'] = \
notification_message.getRelativeUrl()
ticket.RegularisationRequest_triggerDeleteReminderEscalation()
self.assertEqual(
'Visited by RegularisationRequest_checkToTriggerNextEscalationStep ' \
'%s %s %s %s %s %s' % \
(10,
'service_module/slapos_crm_delete_reminder',
'service_module/slapos_crm_delete_acknowledgement',
'Test NM title %s' % new_id,
'Test NM content\n%s\n' % new_id,
'Deleting acknowledgment.'),
ticket.workflow_history['edit_workflow'][-1]['comment'])
class TestSlapOSRegularisationRequest_stopInstanceTreeList(
SlapOSTestCaseMixinWithAbort):
def createInstanceTree(self):
new_id = self.generateNewId()
instance_tree = self.portal.instance_tree_module\
.template_instance_tree.Base_createCloneDocument(batch_mode=1)
instance_tree.edit(
reference="TESTHS-%s" % new_id,
)
instance_tree.validate()
self.portal.portal_workflow._jumpToStateFor(
instance_tree, 'start_requested')
return instance_tree
def test_stopInstanceTreeList_REQUEST_disallowed(self):
ticket = self.createRegularisationRequest()
self.assertRaises(
Unauthorized,
ticket.RegularisationRequest_stopInstanceTreeList,
'footag',
REQUEST={})
@simulate('InstanceTree_stopFromRegularisationRequest',
'person, REQUEST=None',
'context.portal_workflow.doActionFor(' \
'context, action="edit_action", ' \
'comment="Visited by InstanceTree_stopFromRegularisationRequest ' \
'%s" % (person))')
def test_stopInstanceTreeList_matching_subscription(self):
person = self.makePerson(index=0, user=0)
ticket = self.createRegularisationRequest()
instance_tree = self.createInstanceTree()
ticket.edit(
source_project_value=person,
resource='service_module/slapos_crm_stop_acknowledgement',
)
ticket.validate()
ticket.suspend()
instance_tree.edit(
destination_section=person.getRelativeUrl(),
)
self.tic()
result = ticket.\
RegularisationRequest_stopInstanceTreeList('footag')
self.assertTrue(result)
self.tic()
self.assertEqual(
'Visited by InstanceTree_stopFromRegularisationRequest ' \
'%s' % person.getRelativeUrl(),
instance_tree.workflow_history['edit_workflow'][-1]['comment'])
@simulate('InstanceTree_stopFromRegularisationRequest',
'person, REQUEST=None',
'context.portal_workflow.doActionFor(' \
'context, action="edit_action", ' \
'comment="Visited by InstanceTree_stopFromRegularisationRequest ' \
'%s" % (person))')
def test_stopInstanceTreeList_matching_subscription_2(self):
person = self.makePerson(index=0, user=0)
ticket = self.createRegularisationRequest()
instance_tree = self.createInstanceTree()
ticket.edit(
source_project_value=person,
resource='service_module/slapos_crm_delete_reminder',
)
ticket.validate()
ticket.suspend()
instance_tree.edit(
destination_section=person.getRelativeUrl(),
)
self.tic()
result = ticket.\
RegularisationRequest_stopInstanceTreeList('footag')
self.assertTrue(result)
self.tic()
self.assertEqual(
'Visited by InstanceTree_stopFromRegularisationRequest ' \
'%s' % person.getRelativeUrl(),
instance_tree.workflow_history['edit_workflow'][-1]['comment'])
@simulate('InstanceTree_stopFromRegularisationRequest',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_stopInstanceTreeList_other_subscription(self):
person = self.makePerson(index=0, user=0)
ticket = self.createRegularisationRequest()
self.createInstanceTree()
ticket.edit(
source_project_value=person,
resource='service_module/slapos_crm_stop_acknowledgement',
)
ticket.validate()
ticket.suspend()
self.tic()
result = ticket.\
RegularisationRequest_stopInstanceTreeList('footag')
self.assertTrue(result)
self.tic()
@simulate('InstanceTree_stopFromRegularisationRequest',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_stopInstanceTreeList_no_person(self):
ticket = self.createRegularisationRequest()
ticket.edit(
resource='service_module/slapos_crm_stop_acknowledgement',
)
ticket.validate()
ticket.suspend()
self.tic()
result = ticket.\
RegularisationRequest_stopInstanceTreeList('footag')
self.assertFalse(result)
self.tic()
@simulate('InstanceTree_stopFromRegularisationRequest',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_stopInstanceTreeList_not_suspended(self):
person = self.makePerson(index=0, user=0)
ticket = self.createRegularisationRequest()
self.createInstanceTree()
ticket.edit(
source_project_value=person,
resource='service_module/slapos_crm_stop_acknowledgement',
)
ticket.validate()
self.tic()
result = ticket.\
RegularisationRequest_stopInstanceTreeList('footag')
self.assertFalse(result)
self.tic()
@simulate('InstanceTree_stopFromRegularisationRequest',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_stopInstanceTreeList_other_resource(self):
person = self.makePerson(index=0, user=0)
ticket = self.createRegularisationRequest()
self.createInstanceTree()
ticket.edit(
source_project_value=person,
resource='service_module/slapos_crm_acknowledgement',
)
ticket.validate()
ticket.suspend()
self.tic()
result = ticket.\
RegularisationRequest_stopInstanceTreeList('footag')
self.assertFalse(result)
self.tic()
class TestSlapOSInstanceTree_stopFromRegularisationRequest(
SlapOSTestCaseMixinWithAbort):
def createInstanceTree(self):
def createInstanceTree(self, project):
new_id = self.generateNewId()
instance_tree = self.portal.instance_tree_module\
.template_instance_tree.Base_createCloneDocument(batch_mode=1)
.newContent(portal_type="Instance Tree")
instance_tree.edit(
reference="TESTHS-%s" % new_id,
follow_up_value=project,
url_string=self.generateNewSoftwareReleaseUrl(),
title=self.generateNewSoftwareTitle(),
source_reference=self.generateNewSoftwareType(),
text_content=self.generateSafeXml(),
sla_xml=self.generateSafeXml(),
root_slave=False
)
instance_tree.validate()
self.portal.portal_workflow._jumpToStateFor(
instance_tree, 'start_requested')
return instance_tree
def test_stopFromRegularisationRequest_REQUEST_disallowed(self):
def test_stopFromRegularisationRequest_REQUESTdisallowed(self):
self.assertRaises(
Unauthorized,
self.portal.InstanceTree_stopFromRegularisationRequest,
'',
REQUEST={})
def test_stopFromRegularisationRequest_matching_subscription(self):
person = self.makePerson(index=0, user=0)
instance_tree = self.createInstanceTree()
def test_stopFromRegularisationRequest_matchingSubscription(self):
project = self.addProject()
person = self.makePerson(project, index=0, user=0)
instance_tree = self.createInstanceTree(project)
instance_tree.edit(
destination_section=person.getRelativeUrl(),
)
......@@ -1122,9 +475,10 @@ class TestSlapOSInstanceTree_stopFromRegularisationRequest(
self.assertEqual(instance_tree.isRootSlave(), shared)
self.assertEqual(instance_tree.getSlapState(), "stop_requested")
def test_stopFromRegularisationRequest_stopped_subscription(self):
person = self.makePerson(index=0, user=0)
instance_tree = self.createInstanceTree()
def test_stopFromRegularisationRequest_stoppedSubscription(self):
project = self.addProject()
person = self.makePerson(project, index=0, user=0)
instance_tree = self.createInstanceTree(project)
instance_tree.edit(
destination_section=person.getRelativeUrl(),
)
......@@ -1136,38 +490,48 @@ class TestSlapOSInstanceTree_stopFromRegularisationRequest(
self.assertEqual(result, False)
def test_stopFromRegularisationRequest_non_matching_person(self):
instance_tree = self.createInstanceTree()
def test_stopFromRegularisationRequest_nonMatchingPerson(self):
project = self.addProject()
instance_tree = self.createInstanceTree(project)
self.assertRaises(
AssertionError,
instance_tree.InstanceTree_stopFromRegularisationRequest,
'foobar')
class TestSlapOSInstanceTree_deleteFromRegularisationRequest(
SlapOSTestCaseMixinWithAbort):
def createInstanceTree(self):
def createInstanceTree(self, project):
new_id = self.generateNewId()
instance_tree = self.portal.instance_tree_module\
.template_instance_tree.Base_createCloneDocument(batch_mode=1)
.newContent(portal_type="Instance Tree")
instance_tree.edit(
reference="TESTHS-%s" % new_id,
follow_up_value=project,
url_string=self.generateNewSoftwareReleaseUrl(),
title=self.generateNewSoftwareTitle(),
source_reference=self.generateNewSoftwareType(),
text_content=self.generateSafeXml(),
sla_xml=self.generateSafeXml(),
root_slave=False
)
instance_tree.validate()
self.portal.portal_workflow._jumpToStateFor(
instance_tree, 'start_requested')
return instance_tree
def test_deleteFromRegularisationRequest_REQUEST_disallowed(self):
def test_deleteFromRegularisationRequest_REQUESTdisallowed(self):
self.assertRaises(
Unauthorized,
self.portal.InstanceTree_deleteFromRegularisationRequest,
'',
REQUEST={})
def test_deleteFromRegularisationRequest_started_subscription(self):
person = self.makePerson(index=0, user=0)
instance_tree = self.createInstanceTree()
def test_deleteFromRegularisationRequest_startedSubscription(self):
project = self.addProject()
person = self.makePerson(project, index=0, user=0)
instance_tree = self.createInstanceTree(project)
instance_tree.edit(
destination_section=person.getRelativeUrl(),
)
......@@ -1180,6 +544,7 @@ class TestSlapOSInstanceTree_deleteFromRegularisationRequest(
sla_xml = instance_tree.getSlaXml()
shared = instance_tree.isRootSlave()
self.assertEqual(instance_tree.getSlapState(), "start_requested")
self.assertEqual(shared, False)
result = instance_tree.\
InstanceTree_deleteFromRegularisationRequest(person.getRelativeUrl())
......@@ -1193,9 +558,10 @@ class TestSlapOSInstanceTree_deleteFromRegularisationRequest(
self.assertEqual(instance_tree.isRootSlave(), shared)
self.assertEqual(instance_tree.getSlapState(), "destroy_requested")
def test_deleteFromRegularisationRequest_stopped_subscription(self):
person = self.makePerson(index=0, user=0)
instance_tree = self.createInstanceTree()
def test_deleteFromRegularisationRequest_stoppedSubscription(self):
project = self.addProject()
person = self.makePerson(project, index=0, user=0)
instance_tree = self.createInstanceTree(project)
instance_tree.edit(
destination_section=person.getRelativeUrl(),
)
......@@ -1223,9 +589,10 @@ class TestSlapOSInstanceTree_deleteFromRegularisationRequest(
self.assertEqual(instance_tree.isRootSlave(), shared)
self.assertEqual(instance_tree.getSlapState(), "destroy_requested")
def test_deleteFromRegularisationRequest_destroyed_subscription(self):
person = self.makePerson(index=0, user=0)
instance_tree = self.createInstanceTree()
def test_deleteFromRegularisationRequest_destroyedSubscription(self):
project = self.addProject()
person = self.makePerson(project, index=0, user=0)
instance_tree = self.createInstanceTree(project)
instance_tree.edit(
destination_section=person.getRelativeUrl(),
)
......@@ -1237,161 +604,11 @@ class TestSlapOSInstanceTree_deleteFromRegularisationRequest(
self.assertEqual(result, False)
def test_deleteFromRegularisationRequest_non_matching_person(self):
instance_tree = self.createInstanceTree()
def test_deleteFromRegularisationRequest_nonMatchingPerson(self):
project = self.addProject()
instance_tree = self.createInstanceTree(project)
self.assertRaises(
AssertionError,
instance_tree.InstanceTree_deleteFromRegularisationRequest,
'foobar')
class TestSlapOSRegularisationRequest_deleteInstanceTreeList(
SlapOSTestCaseMixinWithAbort):
def createRegularisationRequest(self):
new_id = self.generateNewId()
return self.portal.regularisation_request_module.newContent(
portal_type='Regularisation Request',
title="Test Reg. Req.%s" % new_id,
reference="TESTREGREQ-%s" % new_id,
resource='foo/bar',
)
def createInstanceTree(self):
new_id = self.generateNewId()
instance_tree = self.portal.instance_tree_module\
.template_instance_tree.Base_createCloneDocument(batch_mode=1)
instance_tree.edit(
reference="TESTHS-%s" % new_id,
)
instance_tree.validate()
self.portal.portal_workflow._jumpToStateFor(
instance_tree, 'start_requested')
return instance_tree
def test_deleteInstanceTreeList_REQUEST_disallowed(self):
ticket = self.createRegularisationRequest()
self.assertRaises(
Unauthorized,
ticket.RegularisationRequest_deleteInstanceTreeList,
'footag',
REQUEST={})
@simulate('InstanceTree_deleteFromRegularisationRequest',
'person, REQUEST=None',
'context.portal_workflow.doActionFor(' \
'context, action="edit_action", ' \
'comment="Visited by InstanceTree_deleteFromRegularisationRequest ' \
'%s" % (person))')
def test_deleteInstanceTreeList_matching_subscription(self):
person = self.makePerson(index=0, user=0)
ticket = self.createRegularisationRequest()
instance_tree = self.createInstanceTree()
ticket.edit(
source_project_value=person,
resource='service_module/slapos_crm_delete_acknowledgement',
)
ticket.validate()
ticket.suspend()
instance_tree.edit(
destination_section=person.getRelativeUrl(),
)
self.tic()
result = ticket.\
RegularisationRequest_deleteInstanceTreeList('footag')
self.assertTrue(result)
self.tic()
self.assertEqual(
'Visited by InstanceTree_deleteFromRegularisationRequest ' \
'%s' % person.getRelativeUrl(),
instance_tree.workflow_history['edit_workflow'][-1]['comment'])
@simulate('InstanceTree_deleteFromRegularisationRequest',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_deleteInstanceTreeList_other_subscription(self):
person = self.makePerson(index=0, user=0)
ticket = self.createRegularisationRequest()
self.createInstanceTree()
ticket.edit(
source_project_value=person,
resource='service_module/slapos_crm_delete_acknowledgement',
)
ticket.validate()
ticket.suspend()
self.tic()
result = ticket.\
RegularisationRequest_deleteInstanceTreeList('footag')
self.assertTrue(result)
self.tic()
@simulate('InstanceTree_deleteFromRegularisationRequest',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_deleteInstanceTreeList_no_person(self):
ticket = self.createRegularisationRequest()
ticket.edit(
resource='service_module/slapos_crm_delete_acknowledgement',
)
ticket.validate()
ticket.suspend()
self.tic()
result = ticket.\
RegularisationRequest_deleteInstanceTreeList('footag')
self.assertFalse(result)
self.tic()
@simulate('InstanceTree_deleteFromRegularisationRequest',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_deleteInstanceTreeList_not_suspended(self):
person = self.makePerson(index=0, user=0)
ticket = self.createRegularisationRequest()
self.createInstanceTree()
ticket.edit(
source_project_value=person,
resource='service_module/slapos_crm_delete_acknowledgement',
)
ticket.validate()
self.tic()
result = ticket.\
RegularisationRequest_deleteInstanceTreeList('footag')
self.assertFalse(result)
self.tic()
@simulate('InstanceTree_deleteFromRegularisationRequest',
'*args, **kwargs',
'raise NotImplementedError, "Should not have been called"')
def test_deleteInstanceTreeList_other_resource(self):
person = self.makePerson(index=0, user=0)
ticket = self.createRegularisationRequest()
self.createInstanceTree()
ticket.edit(
source_project_value=person,
resource='service_module/slapos_crm_delete_reminder',
)
ticket.validate()
ticket.suspend()
self.tic()
result = ticket.\
RegularisationRequest_deleteInstanceTreeList('footag')
self.assertFalse(result)
self.tic()
......@@ -23,11 +23,11 @@
import transaction
from erp5.component.test.SlapOSTestCaseMixin import \
SlapOSTestCaseMixin,SlapOSTestCaseMixinWithAbort, simulate
SlapOSTestCaseMixin,SlapOSTestCaseMixinWithAbort, TemporaryAlarmScript, PinnedDateTime
from DateTime import DateTime
from App.Common import rfc1123_date
from Products.ERP5Type.Cache import DEFAULT_CACHE_SCOPE
import json
def getFakeSlapState():
return "destroy_requested"
......@@ -36,7 +36,9 @@ class TestCRMSkinsMixin(SlapOSTestCaseMixinWithAbort):
def afterSetUp(self):
SlapOSTestCaseMixinWithAbort.afterSetUp(self)
self.person = self.makePerson(new_id=self.new_id, index=0, user=0)
self.project = self.addProject()
self.person = self.makePerson(self.project, new_id=self.new_id,
index=0, user=0)
def _cancelTestSupportRequestList(self, title="%"):
for support_request in self.portal.portal_catalog(
......@@ -55,18 +57,19 @@ class TestCRMSkinsMixin(SlapOSTestCaseMixinWithAbort):
assignment.open()
return assignment
def _makeInstanceTree(self):
person = self.portal.person_module.template_member\
.Base_createCloneDocument(batch_mode=1)
def _makeInstanceTree(self, project):
person = self.portal.person_module\
.newContent(portal_type="Person")
instance_tree = self.portal\
.instance_tree_module.template_instance_tree\
.Base_createCloneDocument(batch_mode=1)
.instance_tree_module\
.newContent(portal_type="Instance Tree")
instance_tree.validate()
new_id = self.generateNewId()
instance_tree.edit(
title= "Test hosting sub ticket %s" % new_id,
reference="TESTHST-%s" % new_id,
destination_section_value=person
destination_section_value=person,
follow_up_value=project
)
return instance_tree
......@@ -87,10 +90,9 @@ class TestCRMSkinsMixin(SlapOSTestCaseMixinWithAbort):
def _makeSoftwareInstallation(self):
self._makeComputeNode()
self._makeComputeNode(self.project)
software_installation = self.portal\
.software_installation_module.template_software_installation\
.Base_createCloneDocument(batch_mode=1)
.software_installation_module.newContent(portal_type="Software Installation")
software_installation.edit(
url_string=self.generateNewSoftwareReleaseUrl(),
aggregate=self.compute_node.getRelativeUrl(),
......@@ -109,8 +111,9 @@ class TestSlapOSSupportRequestModule_getMonitoringUrlList(TestCRMSkinsMixin):
# We assume here that several objects created by others tests don't influentiate
# this test.
self.assertEqual(module.SupportRequestModule_getMonitoringUrlList(), [])
instance_tree = self._makeInstanceTree()
self._makeSoftwareInstance(instance_tree, "https://xxx/")
with TemporaryAlarmScript(self.portal, 'Item_getSubscriptionStatus', "'subscribed'"):
instance_tree = self._makeInstanceTree(self.project)
self._makeSoftwareInstance(instance_tree, "https://xxx/")
support_request = module.newContent(portal_type="Support Request")
self.tic()
......@@ -156,100 +159,56 @@ class TestSlapOSSupportRequestModule_getMonitoringUrlList(TestCRMSkinsMixin):
self.tic()
self.assertNotEqual(instance_tree.getSuccessorList(), [])
class TestComputeNode_hasContactedRecently(SlapOSTestCaseMixinWithAbort):
def createSPL(self, compute_node):
delivery_template = self.portal.restrictedTraverse(
self.portal.portal_preferences.getPreferredInstanceDeliveryTemplate())
delivery = delivery_template.Base_createCloneDocument(batch_mode=1)
delivery.edit(
title="TEST SPL COMP %s" % compute_node.getReference(),
start_date=compute_node.getCreationDate(),
)
delivery.newContent(
portal_type="Sale Packing List Line",
title="SPL Line for %s" % compute_node.getReference(),
quantity=1,
aggregate_value_list=compute_node,
)
delivery.confirm(comment="Created from %s" % compute_node.getRelativeUrl())
delivery.start()
delivery.stop()
delivery.deliver()
return delivery
class TestComputeNode_hasContactedRecently(SlapOSTestCaseMixinWithAbort):
def test_ComputeNode_hasContactedRecently_newly_created(self):
compute_node = self._makeComputeNode()[0]
compute_node, _ = self._makeComputeNode(self.addProject())
self.tic()
has_contacted = compute_node.ComputeNode_hasContactedRecently()
self.assertTrue(has_contacted)
@simulate('ComputeNode_getCreationDate', '*args, **kwargs','return DateTime() - 32')
def test_ComputeNode_hasContactedRecently_no_data(self):
compute_node = self._makeComputeNode()[0]
with PinnedDateTime(self, DateTime()-32):
compute_node, _ = self._makeComputeNode(self.addProject())
self.tic()
compute_node.getCreationDate = self.portal.ComputeNode_getCreationDate
has_contacted = compute_node.ComputeNode_hasContactedRecently()
self.assertFalse(has_contacted)
@simulate('ComputeNode_getCreationDate', '*args, **kwargs','return DateTime() - 32')
def test_ComputeNode_hasContactedRecently_memcached(self):
compute_node = self._makeComputeNode()[0]
with PinnedDateTime(self, DateTime()-32):
compute_node, _ = self._makeComputeNode(self.addProject())
compute_node.setAccessStatus("")
self.tic()
compute_node.getCreationDate = self.portal.ComputeNode_getCreationDate
self.tic()
has_contacted = compute_node.ComputeNode_hasContactedRecently()
self.assertTrue(has_contacted)
@simulate('ComputeNode_getCreationDate', '*args, **kwargs','return DateTime() - 32')
def test_ComputeNode_hasContactedRecently_memcached_oudated_no_spl(self):
compute_node = self._makeComputeNode()[0]
try:
self.pinDateTime(DateTime()-32)
with PinnedDateTime(self, DateTime()-32):
compute_node, _ = self._makeComputeNode(self.addProject())
compute_node.setAccessStatus("")
finally:
self.unpinDateTime()
self.tic()
compute_node.getCreationDate = self.portal.ComputeNode_getCreationDate
has_contacted = compute_node.ComputeNode_hasContactedRecently()
self.assertFalse(has_contacted)
@simulate('ComputeNode_getCreationDate', '*args, **kwargs','return DateTime() - 32')
def test_ComputeNode_hasContactedRecently_memcached_oudated_with_spl(self):
compute_node = self._makeComputeNode()[0]
try:
self.pinDateTime(DateTime()-32)
compute_node.setAccessStatus("")
finally:
self.unpinDateTime()
self.createSPL(compute_node)
self.tic()
compute_node.getCreationDate = self.portal.ComputeNode_getCreationDate
has_contacted = compute_node.ComputeNode_hasContactedRecently()
self.assertFalse(has_contacted)
class TestSlapOSisSupportRequestCreationClosed(TestCRMSkinsMixin):
def afterSetUp(self):
TestCRMSkinsMixin.afterSetUp(self)
self.project = self.addProject()
self._cancelTestSupportRequestList()
self.clearCache()
def test_ERP5Site_isSupportRequestCreationClosed(self):
person = self.makePerson(user=0)
other_person = self.makePerson(user=0)
person = self.makePerson(self.project, user=0)
other_person = self.makePerson(self.project, user=0)
url = person.getRelativeUrl()
self.assertFalse(self.portal.ERP5Site_isSupportRequestCreationClosed(url))
self.assertFalse(self.portal.ERP5Site_isSupportRequestCreationClosed())
......@@ -284,7 +243,7 @@ class TestSlapOSisSupportRequestCreationClosed(TestCRMSkinsMixin):
other_person.getRelativeUrl()))
def test_ERP5Site_isSupportRequestCreationClosed_suspended_state(self):
person = self.makePerson(user=0)
person = self.makePerson(self.project, user=0)
url = person.getRelativeUrl()
self.assertFalse(self.portal.ERP5Site_isSupportRequestCreationClosed(url))
self.assertFalse(self.portal.ERP5Site_isSupportRequestCreationClosed())
......@@ -308,7 +267,7 @@ class TestSlapOSisSupportRequestCreationClosed(TestCRMSkinsMixin):
def test_ERP5Site_isSupportRequestCreationClosed_nonmonitoring(self):
person = self.makePerson(user=0)
person = self.makePerson(self.project, user=0)
url = person.getRelativeUrl()
self.assertFalse(self.portal.ERP5Site_isSupportRequestCreationClosed(url))
self.assertFalse(self.portal.ERP5Site_isSupportRequestCreationClosed())
......@@ -331,397 +290,22 @@ class TestSlapOSisSupportRequestCreationClosed(TestCRMSkinsMixin):
self.assertFalse(self.portal.ERP5Site_isSupportRequestCreationClosed())
class TestSlapOSComputeNode_CheckState(TestCRMSkinsMixin):
def beforeTearDown(self):
self._cancelTestSupportRequestList()
transaction.abort()
def afterSetUp(self):
TestCRMSkinsMixin.afterSetUp(self)
self._cancelTestSupportRequestList("% TESTCOMPT-%")
def _makeNotificationMessage(self, reference):
notification_message = self.portal.notification_message_module.newContent(
portal_type="Notification Message",
title='The Compute Node %s has not contacted the server for more than 24 hours' % reference,
text_content='Test NM content<br/>%s<br/>' % reference,
content_type='text/html',
)
return notification_message.getRelativeUrl()
def _getGeneratedSupportRequest(self, compute_node_uid, request_title):
support_request = self.portal.portal_catalog.getResultValue(
portal_type = 'Support Request',
title = request_title,
simulation_state = 'validated',
default_aggregate_uid = compute_node_uid
)
return support_request
@simulate('ERP5Site_isSupportRequestCreationClosed', '*args, **kwargs','return 0')
def test_ComputeNode_checkState_call_support_request(self):
compute_node = self._makeComputeNode(owner=self.makePerson(user=0))[0]
try:
d = DateTime() - 1.1
self.pinDateTime(d)
compute_node.setAccessStatus("")
finally:
self.unpinDateTime()
compute_node_support_request = compute_node.ComputeNode_checkState()
self.assertNotEqual(compute_node_support_request, None)
self.assertIn("[MONITORING] Lost contact with compute_node",
compute_node_support_request.getTitle())
self.assertIn("has not contacted the server for more than 30 minutes",
compute_node_support_request.getDescription())
self.assertIn(d.strftime("%Y/%m/%d %H:%M:%S"),
compute_node_support_request.getDescription())
@simulate('ERP5Site_isSupportRequestCreationClosed', '*args, **kwargs','return 0')
def test_ComputeNode_checkState_empty_cache(self):
compute_node = self._makeComputeNode(owner=self.makePerson(user=0))[0]
compute_node_support_request = compute_node.ComputeNode_checkState()
compute_node_support_request = compute_node.ComputeNode_checkState()
self.assertNotEqual(compute_node_support_request, None)
self.assertIn("[MONITORING] Lost contact with compute_node",
compute_node_support_request.getTitle())
self.assertIn("has not contacted the server (No Contact Information)",
compute_node_support_request.getDescription())
@simulate('ERP5Site_isSupportRequestCreationClosed', '*args, **kwargs','return 0')
@simulate('NotificationTool_getDocumentValue',
'reference=None',
'assert reference == "slapos-crm-compute_node_check_state.notification"\n' \
'return context.restrictedTraverse(' \
'context.REQUEST["test_ComputeNode_checkState_notify"])')
def test_ComputeNode_checkState_notify(self):
compute_node = self._makeComputeNode(owner=self.makePerson(user=0))[0]
person = compute_node.getSourceAdministrationValue()
try:
self.pinDateTime(DateTime()-1.1)
compute_node.setAccessStatus("")
finally:
self.unpinDateTime()
self.portal.REQUEST['test_ComputeNode_checkState_notify'] = \
self._makeNotificationMessage(compute_node.getReference())
compute_node.ComputeNode_checkState()
self.tic()
ticket_title = "[MONITORING] Lost contact with compute_node %s" % compute_node.getReference()
ticket = self._getGeneratedSupportRequest(compute_node.getUid(), ticket_title)
self.assertNotEqual(ticket, None)
event_list = ticket.getFollowUpRelatedValueList()
self.assertEqual(len(event_list), 1)
event = event_list[0]
self.assertEqual(event.getTitle(), ticket.getTitle())
self.assertIn(compute_node.getReference(), event.getTextContent())
self.assertEqual(event.getSource(), person.getRelativeUrl())
self.assertEqual(event.getDestination(), ticket.getSourceSection())
@simulate('ERP5Site_isSupportRequestCreationClosed', '*args, **kwargs','return 0')
@simulate('NotificationTool_getDocumentValue',
'reference=None',
'assert reference == "slapos-crm-compute_node_check_state.notification"\n' \
'return context.restrictedTraverse(' \
'context.REQUEST["test_ComputeNode_checkState_empty_cache_notify"])')
def test_ComputeNode_checkState_empty_cache_notify(self):
compute_node = self._makeComputeNode(owner=self.makePerson(user=0))[0]
person = compute_node.getSourceAdministrationValue()
self.portal.REQUEST['test_ComputeNode_checkState_empty_cache_notify'] = \
self._makeNotificationMessage(compute_node.getReference())
compute_node.ComputeNode_checkState()
self.tic()
ticket_title = "[MONITORING] Lost contact with compute_node %s" % compute_node.getReference()
ticket = self._getGeneratedSupportRequest(compute_node.getUid(), ticket_title)
self.assertNotEqual(ticket, None)
event_list = ticket.getFollowUpRelatedValueList()
self.assertEqual(len(event_list), 1)
event = event_list[0]
self.assertEqual(event.getTitle(), ticket.getTitle())
self.assertIn(compute_node.getReference(), event.getTextContent())
self.assertEqual(event.getDestination(), ticket.getSourceSection())
self.assertEqual(event.getSource(), person.getRelativeUrl())
@simulate('ERP5Site_isSupportRequestCreationClosed', '*args, **kwargs','return 0')
@simulate('NotificationTool_getDocumentValue',
'reference=None',
'assert reference == "slapos-crm-compute_node_check_stalled_instance_state.notification", reference\n' \
'return context.restrictedTraverse(' \
'context.REQUEST["test_ComputeNode_checkState_stalled_instance"])')
def test_ComputeNode_checkState_stalled_instance(self):
compute_node = self._makeComputeNode(owner=self.makePerson(user=0))[0]
self._makeComplexComputeNode()
person = compute_node.getSourceAdministrationValue()
self.portal.REQUEST['test_ComputeNode_checkState_stalled_instance'] = \
self._makeNotificationMessage(compute_node.getReference())
# Computer is getting access
compute_node.setAccessStatus("")
try:
self.pinDateTime(DateTime()-1.1)
self.start_requested_software_instance.setAccessStatus("")
finally:
self.unpinDateTime()
compute_node.ComputeNode_checkState()
self.tic()
ticket_title = "[MONITORING] Compute Node %s has a stalled instance process" % compute_node.getReference()
ticket = self._getGeneratedSupportRequest(compute_node.getUid(), ticket_title)
self.assertNotEqual(ticket, None)
event_list = ticket.getFollowUpRelatedValueList()
self.assertEqual(len(event_list), 1)
event = event_list[0]
self.assertEqual(event.getTitle(), ticket.getTitle())
self.assertIn(compute_node.getReference(), event.getTextContent())
self.assertEqual(event.getDestination(), ticket.getSourceSection())
self.assertEqual(event.getSource(), person.getRelativeUrl())
@simulate('ERP5Site_isSupportRequestCreationClosed', '*args, **kwargs','return 0')
@simulate('NotificationTool_getDocumentValue',
'reference=None',
'assert reference == "slapos-crm-compute_node_check_stalled_instance_state.notification", reference\n' \
'return context.restrictedTraverse(' \
'context.REQUEST["test_ComputeNode_checkState_stalled_instance"])')
def test_ComputeNode_checkState_stalled_instance_single(self):
compute_node = self._makeComputeNode(owner=self.makePerson(user=0))[0]
self._makeComplexComputeNode()
person = compute_node.getSourceAdministrationValue()
self.portal.REQUEST['test_ComputeNode_checkState_stalled_instance'] = \
self._makeNotificationMessage(compute_node.getReference())
# Computer is getting access
compute_node.setAccessStatus("")
try:
self.pinDateTime(DateTime()-1.1)
self.start_requested_software_instance.setAccessStatus("")
self.start_requested_software_installation.setAccessStatus("")
finally:
self.unpinDateTime()
compute_node.ComputeNode_checkState()
self.tic()
ticket_title = "[MONITORING] Compute Node %s has a stalled instance process" % compute_node.getReference()
ticket = self._getGeneratedSupportRequest(compute_node.getUid(), ticket_title)
self.assertNotEqual(ticket, None)
event_list = ticket.getFollowUpRelatedValueList()
self.assertEqual(len(event_list), 1)
event = event_list[0]
self.assertEqual(event.getTitle(), ticket.getTitle())
self.assertIn(compute_node.getReference(), event.getTextContent())
self.assertEqual(event.getDestination(), ticket.getSourceSection())
self.assertEqual(event.getSource(), person.getRelativeUrl())
class TestSlapOSInstanceTree_createSupportRequestEvent(SlapOSTestCaseMixin):
def _makeNotificationMessage(self, reference):
notification_message = self.portal.notification_message_module.newContent(
portal_type="Notification Message",
title='%s created an event' % reference,
text_content='Test NM content<br/>%s<br/>' % reference,
content_type='text/html',
)
return notification_message.getRelativeUrl()
def _makeInstanceTree(self):
person = self.makePerson(user=1)
instance_tree = self.portal\
.instance_tree_module.template_instance_tree\
.Base_createCloneDocument(batch_mode=1)
instance_tree.validate()
new_id = self.generateNewId()
instance_tree.edit(
title= "Test hosting sub ticket %s" % new_id,
reference="TESTHST-%s" % new_id,
destination_section_value=person
)
return instance_tree
def _getGeneratedSupportRequest(self, instance_tree_uid):
support_request = self.portal.portal_catalog.getResultValue(
portal_type = 'Support Request',
simulation_state = "validated",
default_aggregate_uid = instance_tree_uid
)
return support_request
@simulate('ERP5Site_isSupportRequestCreationClosed', '*args, **kwargs','return 0')
@simulate('SoftwareInstance_hasReportedError', '*args, **kwargs','return "MSG"')
@simulate('NotificationTool_getDocumentValue',
'reference=None',
'assert reference == "test-slapos-crm-check.notification"\n' \
'return context.restrictedTraverse(' \
'context.REQUEST["testInstanceTree_createSupportRequestEvent"])')
def testInstanceTree_createSupportRequestEvent(self):
instance_tree = self._makeInstanceTree()
person = instance_tree.getDestinationSectionValue()
self.portal.REQUEST['testInstanceTree_createSupportRequestEvent'] = \
self._makeNotificationMessage(instance_tree.getReference())
instance_tree.InstanceTree_createSupportRequestEvent(
instance_tree, "test-slapos-crm-check.notification")
self.tic()
ticket_title = "Instance Tree %s is failing." % instance_tree.getTitle()
ticket = self._getGeneratedSupportRequest(
instance_tree.getUid())
self.assertNotEqual(ticket, None)
self.assertEqual(ticket.getSimulationState(), "validated")
self.assertNotEqual(ticket, None)
event_list = ticket.getFollowUpRelatedValueList()
self.assertEqual(len(event_list), 1)
event = event_list[0]
self.assertEqual(event.getTitle(), ticket_title)
self.assertIn(instance_tree.getReference(), event.getTextContent())
self.assertEqual(event.getSource(), person.getRelativeUrl())
self.assertEqual(event.getDestination(), ticket.getSourceSection())
ticket.suspend()
self.tic()
self.assertEqual(None, self._getGeneratedSupportRequest(
instance_tree.getUid()))
instance_tree.InstanceTree_createSupportRequestEvent(
instance_tree, "test-slapos-crm-check.notification")
self.tic()
ticket = self._getGeneratedSupportRequest(
instance_tree.getUid())
# Do not reopen the ticket if it is suspended
self.assertEqual(None, ticket)
@simulate('ERP5Site_isSupportRequestCreationClosed', '*args, **kwargs','return 1')
@simulate('SoftwareInstance_hasReportedError', '*args, **kwargs','return "MSG"')
def testInstanceTree_createSupportRequestEvent_closed(self):
instance_tree = self._makeInstanceTree()
self.assertEqual(None,
instance_tree.InstanceTree_createSupportRequestEvent(
instance_tree, "test-slapos-crm-check.notification"))
@simulate('ERP5Site_isSupportRequestCreationClosed', '*args, **kwargs','return 0')
@simulate('SoftwareInstance_hasReportedError', '*args, **kwargs','return "MSG"')
def testInstanceTree_createSupportRequestEvent_no_person(self):
instance_tree = self._makeInstanceTree()
instance_tree.setDestinationSectionValue(None)
self.assertEqual(None,
instance_tree.InstanceTree_createSupportRequestEvent(
instance_tree, "test-slapos-crm-check.notification"))
class TestSlapOSHasError(SlapOSTestCaseMixin):
def _makeSoftwareRelease(self, software_release_url=None):
software_release = self.portal.software_release_module\
.template_software_release.Base_createCloneDocument(batch_mode=1)
new_id = self.generateNewId()
software_release.edit(
url_string=software_release_url or self.generateNewSoftwareReleaseUrl(),
reference='TESTSOFTRELS-%s' % new_id,
title='Start requested for %s' % new_id
)
software_release.release()
return software_release
def _makeSoftwareInstallation(self, software_release_url):
software_installation = self.portal\
.software_installation_module.template_software_installation\
.Base_createCloneDocument(batch_mode=1)
new_id = self.generateNewId()
software_installation.edit(
url_string=software_release_url,
aggregate=self.compute_node.getRelativeUrl(),
reference='TESTSOFTINSTS-%s' % new_id,
title='Start requested for %s' % self.compute_node.getUid()
)
software_installation.validate()
software_installation.requestStart()
return software_installation
def _makeSoftwareInstance(self, instance_tree, software_url):
kw = dict(
software_release=software_url,
software_type=self.generateNewSoftwareType(),
instance_xml=self.generateSafeXml(),
sla_xml=self.generateSafeXml(),
shared=False,
software_title=instance_tree.getTitle(),
state='started'
)
instance_tree.requestStart(**kw)
instance_tree.requestInstance(**kw)
def _makeInstanceTree(self):
person = self.portal.person_module.template_member\
.Base_createCloneDocument(batch_mode=1)
instance_tree = self.portal\
.instance_tree_module.template_instance_tree\
.Base_createCloneDocument(batch_mode=1)
instance_tree.validate()
new_id = self.generateNewId()
instance_tree.edit(
title= "Test hosting sub ticket %s" % new_id,
reference="TESTHST-%s" % new_id,
destination_section_value=person
)
return instance_tree
def _makeComputePartitionList(self):
for i in range(1, 5):
id_ = 'partition%s' % (i, )
p = self.compute_node.newContent(portal_type='Compute Partition',
id=id_,
title=id_,
reference=id_,
default_network_address_ip_address='ip_address_%s' % i,
default_network_address_netmask='netmask_%s' % i)
p.markFree()
p.validate()
def test_SoftwareInstance_hasReportedError(self):
instance_tree = self._makeInstanceTree()
self._makeSoftwareInstance(instance_tree,
self.generateNewSoftwareReleaseUrl())
instance = instance_tree.getSuccessorValue()
instance = self.portal.software_instance_module.newContent(
portal_type="Software Instance",
reference=self.generateNewId()
)
_, partition = self._makeComputeNode(self.addProject())
self._makeComputeNode()
self._makeComputePartitionList()
error_date = DateTime() - 0.1
with PinnedDateTime(self, error_date):
instance.setErrorStatus("")
instance.setErrorStatus("")
self.assertEqual(instance.SoftwareInstance_hasReportedError(), None)
instance.setAggregateValue(self.compute_node.partition1)
instance.setAggregateValue(partition)
self.assertEqual(str(instance.SoftwareInstance_hasReportedError()), '#error ')
......@@ -729,21 +313,16 @@ class TestSlapOSHasError(SlapOSTestCaseMixin):
self.assertEqual(instance.SoftwareInstance_hasReportedError(), None)
def test_SoftwareInstallation_hasReportedError(self):
software_release = self._makeSoftwareRelease()
self._makeComputeNode()
installation = self._makeSoftwareInstallation(
software_release.getUrlString()
installation = self.portal.software_installation_module.newContent(
reference=self.generateNewId()
)
self.assertEqual(installation.SoftwareInstallation_hasReportedError(), None)
error_date = DateTime()
try:
self.pinDateTime(error_date)
error_date = DateTime() - 0.1
with PinnedDateTime(self, error_date):
installation.setErrorStatus("")
finally:
self.unpinDateTime()
self.assertNotEqual(installation.SoftwareInstallation_hasReportedError(), None)
self.assertEqual(
rfc1123_date(installation.SoftwareInstallation_hasReportedError()),
rfc1123_date(error_date))
......@@ -751,202 +330,15 @@ class TestSlapOSHasError(SlapOSTestCaseMixin):
self.assertEqual(installation.SoftwareInstallation_hasReportedError(), None)
@simulate('ERP5Site_isSupportRequestCreationClosed', '','return 0')
@simulate('InstanceTree_createSupportRequestEvent',
'instance, notification_message_reference',
'return "Visited by InstanceTree_createSupportRequestEvent ' \
'%s %s" % (instance.getUid(), notification_message_reference)')
def testInstanceTree_checkSoftwareInstanceState(self):
date = DateTime()
def getCreationDate(*args, **kwargs):
return date - 2
from Products.ERP5Type.Base import Base
original_get_creation = Base.getCreationDate
Base.getCreationDate = getCreationDate
try:
instance_tree = self._makeInstanceTree()
self.assertEqual(instance_tree.getCreationDate(), date - 2)
self._makeSoftwareInstance(instance_tree,
self.generateNewSoftwareReleaseUrl())
instance = instance_tree.getSuccessorValue()
self.assertEqual(instance.getCreationDate(), date - 2)
self._makeComputeNode()
self._makeComputePartitionList()
instance.setAggregateValue(self.compute_node.partition1)
error_date = DateTime()
value = json.dumps(
{"created_at":"%s" % error_date, "text": "#error ", "since": "%s" % (error_date - 2)}
)
cache_duration = instance._getAccessStatusCacheFactory().cache_duration
instance._getAccessStatusPlugin().set(instance._getAccessStatusCacheKey(),
DEFAULT_CACHE_SCOPE, value, cache_duration=cache_duration)
self.assertEqual(
'Visited by InstanceTree_createSupportRequestEvent %s %s' % \
(instance.getUid(),
"slapos-crm-instance-tree-instance-state.notification"),
instance_tree.InstanceTree_checkSoftwareInstanceState())
instance.setAccessStatus("")
self.assertEqual(None,
instance_tree.InstanceTree_checkSoftwareInstanceState())
finally:
Base.getCreationDate = original_get_creation
self.portal.portal_types.resetDynamicDocumentsOnceAtTransactionBoundary()
transaction.commit()
@simulate('ERP5Site_isSupportRequestCreationClosed', '','return 0')
@simulate('InstanceTree_createSupportRequestEvent',
'instance, notification_message_reference',
'return "Visited by InstanceTree_createSupportRequestEvent ' \
'%s %s" % (instance.getUid(), notification_message_reference)')
def testInstanceTree_checkSoftwareInstanceState_tolerance(self):
date = DateTime()
def getCreationDate(*args, **kwargs):
return date - 2
from Products.ERP5Type.Base import Base
original_get_creation = Base.getCreationDate
Base.getCreationDate = getCreationDate
try:
instance_tree = self._makeInstanceTree()
self.assertEqual(instance_tree.getCreationDate(), date - 2)
self._makeSoftwareInstance(instance_tree,
self.generateNewSoftwareReleaseUrl())
instance = instance_tree.getSuccessorValue()
self.assertEqual(instance.getCreationDate(), date - 2)
self._makeComputeNode()
self._makeComputePartitionList()
instance.setAggregateValue(self.compute_node.partition1)
memcached_dict = self.portal.portal_memcached.getMemcachedDict(
key_prefix='slap_tool',
plugin_path='portal_memcached/default_memcached_plugin')
error_date = DateTime()
memcached_dict[instance.getReference()] = json.dumps(
{"created_at":"%s" % error_date, "text": "#error ", "since": "%s" % error_date}
)
# With tolerance of 30 min this should create SupportRequests immediately
self.assertEqual(None,
instance_tree.InstanceTree_checkSoftwareInstanceState())
finally:
Base.getCreationDate = original_get_creation
self.portal.portal_types.resetDynamicDocumentsOnceAtTransactionBoundary()
transaction.commit()
@simulate('ERP5Site_isSupportRequestCreationClosed', '','return 0')
@simulate('InstanceTree_createSupportRequestEvent',
'instance, notification_message_reference',
'return "Visited by InstanceTree_createSupportRequestEvent ' \
'%s %s" % (instance.getRelativeUrl(), notification_message_reference)')
def testInstanceTree_checkSoftwareInstanceState_partially_allocation(self):
date = DateTime()
def getCreationDate(*args, **kwargs):
return date - 2
from Products.ERP5Type.Base import Base
original_get_creation = Base.getCreationDate
Base.getCreationDate = getCreationDate
try:
instance_tree = self._makeInstanceTree()
self.assertEqual(instance_tree.getCreationDate(), date - 2)
self._makeSoftwareInstance(instance_tree,
self.generateNewSoftwareReleaseUrl())
instance = instance_tree.getSuccessorValue()
self.assertEqual(instance.getCreationDate(), date - 2)
self._makeComputeNode()
self._makeComputePartitionList()
instance.setAggregateValue(self.compute_node.partition1)
kw = dict(
software_release=instance_tree.getUrlString(),
software_type=self.generateNewSoftwareType(),
instance_xml=self.generateSafeXml(),
sla_xml=self.generateSafeXml(),
shared=False,
software_title="Another INstance %s" % self.generateNewId(),
state='started'
)
instance.requestInstance(**kw)
self.tic()
instance.setAccessStatus("")
self.assertEqual(
'Visited by InstanceTree_createSupportRequestEvent %s %s' % \
(instance.getSuccessor(portal_type="Software Instance"),
"slapos-crm-instance-tree-instance-allocation.notification"),
instance_tree.InstanceTree_checkSoftwareInstanceState())
kw["state"] = "destroyed"
instance.requestInstance(**kw)
self.tic()
self.assertEqual(
None,
instance_tree.InstanceTree_checkSoftwareInstanceState())
finally:
Base.getCreationDate = original_get_creation
self.portal.portal_types.resetDynamicDocumentsOnceAtTransactionBoundary()
transaction.commit()
@simulate('ERP5Site_isSupportRequestCreationClosed', '','return 0')
def testInstanceTree_checkSoftwareInstanceState_too_early(self):
instance_tree = self._makeInstanceTree()
self._makeSoftwareInstance(instance_tree,
self.generateNewSoftwareReleaseUrl())
instance = instance_tree.getSuccessorValue()
self._makeComputeNode()
self._makeComputePartitionList()
instance.setAggregateValue(self.compute_node.partition1)
instance.setErrorStatus("")
self.assertEqual(
None,
instance_tree.InstanceTree_checkSoftwareInstanceState())
class TestCRMPropertySheetConstraint(SlapOSTestCaseMixin):
def afterSetUp(self):
SlapOSTestCaseMixin.afterSetUp(self)
portal = self.getPortalObject()
self.project = self.addProject()
self.ticket_trade_condition = portal.sale_trade_condition_module.slapos_ticket_trade_condition
person_user = self.makePerson()
person_user = self.makePerson(self.project)
self.tic()
# Login as new user
......@@ -957,8 +349,9 @@ class TestCRMPropertySheetConstraint(SlapOSTestCaseMixin):
self.support_request = portal.support_request_module.newContent(
portal_type="Support Request",
destination_decision=person_user.getRelativeUrl(),
specialise=self.ticket_trade_condition.getRelativeUrl()
destination_value=person_user,
destination_decision_value=person_user,
#specialise=
)
# Value set by the init
......@@ -968,51 +361,26 @@ class TestCRMPropertySheetConstraint(SlapOSTestCaseMixin):
def beforeTearDown(self):
transaction.abort()
def testCheckCausalitySourceDestinationConsistency(self):
person = self.portal.portal_membership.getAuthenticatedMember().getUserValue()
self.support_request.approveRegistration()
self.tic()
self.logout()
self.login()
event = self.support_request.getCausalityValue()
self.assertNotEqual(event, None)
self.assertFalse(event.checkConsistency())
self.assertFalse(self.support_request.checkConsistency())
source = event.getDestination()
event.setDestination(person.getRelativeUrl())
non_consistency_list = [str(i.getTranslatedMessage()) for i in self.support_request.checkConsistency()]
self.assertEqual(non_consistency_list,
['Destination of the related event should be the slapos organisation'])
event.setSource(source)
non_consistency_list = [str(i.getTranslatedMessage()) for i in self.support_request.checkConsistency()]
self.assertEqual(non_consistency_list,
['Sender of the related event should be the customer',
'Destination of the related event should be the slapos organisation'])
def testCheckCustomerAsSourceOrDestinationConsistency(self):
person = self.portal.portal_membership.getAuthenticatedMember().getUserValue()
self.support_request.approveRegistration()
self.support_request.Ticket_createProjectEvent(
"foo", "incoming", "Web Message",
self.portal.service_module.slapos_crm_information.getRelativeUrl(),
"bar", "text/plain"
)
self.tic()
self.logout()
self.login()
event = self.support_request.getCausalityValue()
event = self.support_request.getFollowUpRelatedValue()
self.assertNotEqual(event, None)
self.assertFalse(event.checkConsistency())
self.assertFalse(self.support_request.checkConsistency())
person_user = self.makePerson()
person_user = self.makePerson(self.project)
self.tic()
event.setSource(person_user.getRelativeUrl())
......@@ -1025,101 +393,15 @@ class TestCRMPropertySheetConstraint(SlapOSTestCaseMixin):
event.setDestination(person.getRelativeUrl())
self.assertFalse(event.checkConsistency())
class TestSupportRequestUpdateMonitoringState(SlapOSTestCaseMixin):
def _makeInstanceTree(self):
person = self.portal.person_module.template_member\
.Base_createCloneDocument(batch_mode=1)
instance_tree = self.portal\
.instance_tree_module.template_instance_tree\
.Base_createCloneDocument(batch_mode=1)
instance_tree.validate()
new_id = self.generateNewId()
instance_tree.edit(
title= "Test hosting sub ticket %s" % new_id,
reference="TESTHST-%s" % new_id,
destination_section_value=person
)
return instance_tree
def _makeSupportRequest(self):
return self.portal.restrictedTraverse(
self.portal.portal_preferences.getPreferredSupportRequestTemplate()).\
Base_createCloneDocument(batch_mode=1)
@simulate('ERP5Site_isSupportRequestCreationClosed', '','return 0')
@simulate('SupportRequest_updateMonitoringDestroyRequestedState',
"",
'return "Visited by SupportRequest_updateMonitoringDestroyRequestedState '\
'%s" % (context.getRelativeUrl(),)')
def testSupportRequest_updateMonitoringState(self):
support_request = self._makeSupportRequest()
self.assertEqual(None,
support_request.SupportRequest_updateMonitoringState())
support_request.validate()
self.assertEqual(None,
support_request.SupportRequest_updateMonitoringState())
hs = self._makeInstanceTree()
support_request.setAggregateValue(hs)
hs.getSlapState = getFakeSlapState
self.assertEqual(
"Visited by SupportRequest_updateMonitoringDestroyRequestedState %s" %\
support_request.getRelativeUrl(),
support_request.SupportRequest_updateMonitoringState())
support_request.invalidate()
self.assertEqual(None,
support_request.SupportRequest_updateMonitoringState())
@simulate('ERP5Site_isSupportRequestCreationClosed', '','return 0')
def testSupportRequest_updateMonitoringDestroyRequestedState(self):
support_request = self._makeSupportRequest()
self.assertEqual(None,
support_request.SupportRequest_updateMonitoringDestroyRequestedState())
support_request.validate()
self.assertEqual(None,
support_request.SupportRequest_updateMonitoringDestroyRequestedState())
support_request.setAggregateValue(
self._makeComputeNode(owner=self.makePerson(user=0))[0])
support_request.setDestinationDecisionValue(self.makePerson(user=0))
self.assertEqual(None,
support_request.SupportRequest_updateMonitoringDestroyRequestedState())
hs = self._makeInstanceTree()
support_request.setAggregateValue(hs)
self.tic()
hs.getSlapState = getFakeSlapState
self.commit()
self.assertNotEqual(None,
support_request.SupportRequest_updateMonitoringDestroyRequestedState())
self.tic()
event_list = support_request.getFollowUpRelatedValueList()
self.assertEqual(len(event_list), 1)
event = event_list[0]
self.assertEqual(event.getTitle(), 'Instance Tree was destroyed was destroyed by the user')
self.assertEqual(event.getSource(), support_request.getDestinationDecision())
self.assertEqual(event.getDestination(), support_request.getSourceSection())
self.assertEqual("invalidated",
support_request.getSimulationState())
class TestSlapOSFolder_getTicketFeedUrl(TestCRMSkinsMixin):
def _test(self, module):
self.assertRaises(ValueError, module.Folder_getTicketFeedUrl)
person = self.makePerson(user=1)
person = self.makePerson(self.project, user=1)
self.addProjectProductionManagerAssignment(person, self.project)
self.tic()
self.login(person.getUserId())
url = module.Folder_getTicketFeedUrl()
......@@ -1141,235 +423,3 @@ class TestSlapOSFolder_getTicketFeedUrl(TestCRMSkinsMixin):
def test_Folder_getTicketFeedUrl_incident_response_module(self):
self._test(self.portal.incident_response_module)
class TestSlapOSPerson_getSlapOSPendingTicket(TestCRMSkinsMixin):
def test_getSlapOSPendingTicket_support_request(self):
person = self.makePerson()
ticket = self.portal.support_request_module.newContent(\
title="Test Support Request %s" % self.new_id,
destination_decision=person.getRelativeUrl())
pending_ticket_list = person.Person_getSlapOSPendingTicket()
# Not indexed yet
self.assertEqual(len(pending_ticket_list), 0)
self.tic()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
ticket.submit()
ticket.immediateReindexObject()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
ticket.validate()
ticket.immediateReindexObject()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
ticket.suspend()
ticket.immediateReindexObject()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 1)
self.assertEqual(pending_ticket_list[0].getUid(), ticket.getUid())
ticket.invalidate()
ticket.immediateReindexObject()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
def test_getSlapOSPendingTicket_support_request_cancelled(self):
person = self.makePerson()
ticket = self.portal.support_request_module.newContent(\
title="Test Support Request %s" % self.new_id,
destination_decision=person.getRelativeUrl())
pending_ticket_list = person.Person_getSlapOSPendingTicket()
# Not indexed yet
self.assertEqual(len(pending_ticket_list), 0)
self.tic()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
ticket.submit()
ticket.immediateReindexObject()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
ticket.cancel()
ticket.immediateReindexObject()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
def test_getSlapOSPendingTicket_upgrade_decision(self):
def newUpgradeDecision():
ticket = self.portal.upgrade_decision_module.newContent(
portal_type='Upgrade Decision',
title="Upgrade Decision Test %s" % self.new_id,
reference="TESTUD-%s" % self.new_id)
ticket.immediateReindexObject()
return ticket
person = self.makePerson()
ticket = newUpgradeDecision()
ticket.setDestinationDecisionValue(person)
ticket.newContent(
portal_type="Upgrade Decision Line"
)
pending_ticket_list = person.Person_getSlapOSPendingTicket()
# Not indexed yet
self.assertEqual(len(pending_ticket_list), 0)
self.tic()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
ticket.plan()
ticket.immediateReindexObject()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
ticket.confirm()
ticket.immediateReindexObject()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 1)
self.assertEqual(pending_ticket_list[0].getUid(), ticket.getUid())
ticket.start()
ticket.immediateReindexObject()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
ticket.stop()
ticket.immediateReindexObject()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
ticket.deliver()
ticket.immediateReindexObject()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
def test_getSlapOSPendingTicket_upgrade_decision_cancel(self):
def newUpgradeDecision():
ticket = self.portal.upgrade_decision_module.newContent(
portal_type='Upgrade Decision',
title="Upgrade Decision Test %s" % self.new_id,
reference="TESTUD-%s" % self.new_id)
ticket.immediateReindexObject()
return ticket
person = self.makePerson()
ticket = newUpgradeDecision()
ticket.setDestinationDecisionValue(person)
ticket.newContent(
portal_type="Upgrade Decision Line"
)
pending_ticket_list = person.Person_getSlapOSPendingTicket()
# Not indexed yet
self.assertEqual(len(pending_ticket_list), 0)
self.tic()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
ticket.plan()
ticket.immediateReindexObject()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
ticket.cancel()
ticket.immediateReindexObject()
pending_ticket_list = person.Person_getSlapOSPendingTicket()
self.assertEqual(len(pending_ticket_list), 0)
class TestSlapOSPerson_getSlapOSPendingTicketMessageTemplate(TestCRMSkinsMixin):
@simulate('NotificationTool_getDocumentValue',
'reference=None',
'assert reference == "slapos-crm-person-pending-ticket-notification"\n' \
'return None')
@simulate('Person_getSlapOSPendingTicket', '*args, **kwargs','return range(99)')
def test_getSlapOSPendingTicketMessageTemplate(self):
person = self.makePerson()
# Test without notification message
title, message = person.Person_getSlapOSPendingTicketMessageTemplate()
self.assertEqual(""" You have 99 pending tickets """, title)
self.assertEqual(""" You have 99 pending tickets """, message)
def _makeNotificationMessage(self):
notification_message = self.portal.notification_message_module.newContent(
portal_type="Notification Message",
title='Pending ticket',
text_content_substitution_mapping_method_id='NotificationMessage_getSubstitutionMappingDictFromArgument',
text_content='Test NM content ${username} AMOUNT (${amount}) WEBSITE(${website})',
content_type='text/plain',
)
return notification_message.getRelativeUrl()
@simulate('Person_getSlapOSPendingTicket', '*args, **kwargs','return range(99)')
@simulate('NotificationTool_getDocumentValue',
'reference=None',
'assert reference == "slapos-crm-person-pending-ticket-notification"\n' \
'return context.restrictedTraverse(' \
'context.REQUEST["test_getSlapOSPendingTicketMessageTemplate"])')
def test_getSlapOSPendingTicketMessageTemplate_with_notification_message(self):
person = self.makePerson()
self.portal.REQUEST['test_getSlapOSPendingTicketMessageTemplate'] = \
self._makeNotificationMessage()
title, message = person.Person_getSlapOSPendingTicketMessageTemplate()
self.assertEqual('Pending ticket', title)
web_site_url = self.portal.portal_preferences.getPreferredSlaposWebSiteUrl()
self.assertEqual(message,
'Test NM content Member Template AMOUNT (99) WEBSITE(%s)' % web_site_url)
class TestSlapOSPerson_sendPendingTicketReminder(TestCRMSkinsMixin):
@simulate('Person_getSlapOSPendingTicket', '*args, **kwargs','return []')
@simulate('Person_sendSlapOSPendingTicketNotification', '*args, **kwargs','assert False')
def test_sendPendingTicketReminder(self):
person = self.makePerson()
# Script Person_sendSlapOSPendingTicketNotification not called
person.Person_sendPendingTicketReminder()
@simulate('Person_getSlapOSPendingTicket', '*args, **kwargs','return [1]')
@simulate('Person_sendSlapOSPendingTicketNotification', '*args, **kwargs',
'context.REQUEST.set("test_getSlapOSPendingTicketMessageTemplate", "called")')
def test_sendPendingTicketReminder_positive_amount(self):
person = self.makePerson()
person.Person_sendPendingTicketReminder()
self.assertEqual(self.portal.REQUEST["test_getSlapOSPendingTicketMessageTemplate"],
"called")
class TestSlapOSPerson_sendSlapOSPendingTicketNotification(TestCRMSkinsMixin):
def test_sendSlapOSPendingTicketNotification(self):
person = self.makePerson()
event = person.Person_sendSlapOSPendingTicketNotification(
"TEST TITLE",
"TEST CONTENT",
batch_mode=1
)
self.tic()
self.assertEqual(event.getTitle(), "TEST TITLE")
self.assertEqual(event.getTextContent(), "TEST CONTENT")
self.assertEqual(event.getSimulationState(), "started")
self.assertEqual(event.getPortalType(), "Mail Message")
self.assertEqual(event.getDestination(), person.getRelativeUrl())
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2002-2012 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from erp5.component.test.SlapOSTestCaseMixin import SlapOSTestCaseMixin #, simulate
import transaction
class TestSlapOSCoreTicketSlapInterfaceWorkflow(SlapOSTestCaseMixin):
def afterSetUp(self):
SlapOSTestCaseMixin.afterSetUp(self)
portal = self.getPortalObject()
self.ticket_trade_condition = portal.sale_trade_condition_module.slapos_ticket_trade_condition
person_user = self.makePerson()
self.tic()
# Login as new user
self.login(person_user.getUserId())
new_person = self.portal.portal_membership.getAuthenticatedMember().getUserValue()
self.assertEqual(person_user.getRelativeUrl(), new_person.getRelativeUrl())
self.support_request = portal.support_request_module.newContent(
portal_type="Support Request",
destination_decision=person_user.getRelativeUrl(),
specialise=self.ticket_trade_condition.getRelativeUrl()
)
# Value set by the init
self.assertTrue(self.support_request.getReference().startswith("SR-"),
"Reference don't start with SR- : %s" % self.support_request.getReference())
def beforeTearDown(self):
transaction.abort()
def test_SupportRequest_approveRegistration_no_reference(self):
self.support_request.setReference(None)
self.assertRaises(ValueError, self.support_request.approveRegistration)
def test_SupportRequest_approveRegistration_already_validated(self):
# Login as admin since user cannot re-approve a validated project
self.login()
self.support_request.validate()
# Don't raise if support request is validated
self.assertEqual(self.support_request.approveRegistration(), None)
def test_SupportRequest_approveRegistration(self):
person = self.portal.portal_membership.getAuthenticatedMember().getUserValue()
self.support_request.approveRegistration()
self.tic()
self.logout()
self.login(person.getUserId())
self.assertEqual(self.support_request.getSimulationState(),
'validated')
self.assertEqual(self.support_request.getSourceSection(),
self.ticket_trade_condition.getSourceSection())
self.assertEqual(self.support_request.getSourceTrade(),
self.ticket_trade_condition.getSourceSection())
self.assertEqual(self.support_request.getSource(),
self.ticket_trade_condition.getSource())
self.assertNotEqual(self.support_request.getStartDate(),
None)
event = self.support_request.getCausalityValue()
self.assertNotEqual(event, None)
event_relative_url = self.support_request.REQUEST.get("event_relative_url")
self.assertEqual(event.getRelativeUrl(), event_relative_url)
self.assertEqual(event.getTitle(), self.support_request.getTitle())
def test_SupportRequest_requestEvent_noParameter(self):
self.assertRaises(TypeError, self.support_request.requestEvent)
self.assertRaises(TypeError, self.support_request.requestEvent, event_title="A")
self.assertRaises(TypeError, self.support_request.requestEvent, event_content="A")
self.assertRaises(TypeError, self.support_request.requestEvent, event_source="A")
def test_SupportRequest_requestEvent(self):
person = self.portal.portal_membership.getAuthenticatedMember().getUserValue()
self.support_request.approveRegistration()
self.tic()
self.logout()
self.login(person.getUserId())
self.support_request.requestEvent(
event_title="A",
event_content="B",
event_source=person.getRelativeUrl())
self.tic()
event_relative_url = self.support_request.REQUEST.get("event_relative_url")
event = self.portal.restrictedTraverse(event_relative_url)
self.assertEqual(event.getSimulationState(), "stopped")
self.assertEqual(self.support_request.getSimulationState(),
'validated')
self.assertEqual(self.support_request.getDestinationDecision(),
event.getSource())
self.assertEqual(person, event.getSourceValue())
self.assertEqual(self.support_request.getResource(),
event.getResource())
self.assertEqual(self.support_request,
event.getFollowUpValue())
self.assertEqual(event.getTitle(), "A")
self.assertEqual(event.getTextContent(), "B")
self.assertEqual(event.getContentType(), "text/plain")
self.assertEqual(event.getPortalType(), "Web Message")
self.assertEqual(event.getDestination(),
self.support_request.getSource())
self.assertNotEqual(event.getStartDate(),
None)
def test_SupportRequest_notify_noParameter(self):
self.assertRaises(TypeError, self.support_request.notify)
self.assertRaises(TypeError, self.support_request.notify, message_title="A")
self.assertRaises(TypeError, self.support_request.notify, message="A")
self.assertRaises(TypeError, self.support_request.notify, destination_relative_url="A")
def test_SupportRequest_notify(self):
person = self.portal.portal_membership.getAuthenticatedMember().getUserValue()
self.support_request.approveRegistration()
self.tic()
self.logout()
self.login()
self.support_request.notify(
message_title="A",
message="B")
event = self.support_request.REQUEST.get("ticket_notified_item")
self.assertEqual(event.getSimulationState(), "delivered")
self.assertEqual(self.support_request.getSimulationState(),
'validated')
self.assertEqual(self.support_request.getSourceSection(),
event.getDestination())
self.assertEqual(person, event.getSourceValue())
self.assertEqual("service_module/slapos_crm_information",
event.getResource())
self.assertEqual(self.support_request,
event.getFollowUpValue())
self.assertEqual(event.getTitle(), "A")
self.assertEqual(event.getTextContent(), "B")
self.assertEqual(event.getContentType(), "text/html")
self.assertEqual(event.getPortalType(), "Web Message")
self.assertEqual(event.getSource(),
self.support_request.getDestinationDecision())
self.assertEqual(event.getDestination(),
self.support_request.getSourceSection())
self.assertNotEqual(event.getStartDate(),
None)
# Retry now to see if doesn't create a new message
self.support_request.notify(
message_title="A",
message="B")
self.tic()
self.assertEqual(event,
self.support_request.REQUEST.get("ticket_notified_item"))
# Retry, now it must create a new one
self.support_request.notify(
message_title="C",
message="B")
self.tic()
self.assertNotEqual(event,
self.support_request.REQUEST.get("ticket_notified_item"))
# Remove completly the ticket_notified_item and try to create a new one
# It should find it anyway from catalog.
self.support_request.REQUEST.set("ticket_notified_item", None)
self.commit()
# Retry, now it must create a new one
self.support_request.notify(
message_title="A",
message="B")
self.tic()
self.assertEqual(event,
self.support_request.REQUEST.get("ticket_notified_item"))
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2022 Nexedi SA and Contributors. All Rights Reserved.
#
##############################################################################
from erp5.component.test.testSlapOSERP5VirtualMasterScenario import TestSlapOSVirtualMasterScenarioMixin
from erp5.component.test.SlapOSTestCaseMixin import PinnedDateTime
from DateTime import DateTime
class TestSlapOSCRMScenario(TestSlapOSVirtualMasterScenarioMixin):
def test_notPaidInvoiceScenario(self):
"""
Ensure services are destroyed, open order are archived, and
deposit is used to pay the invoice
"""
creation_date = DateTime('2020/05/19')
with PinnedDateTime(self, creation_date):
owner_person, currency, project = self.bootstrapAccountingTest()
# Create software product
software_release_url = self.generateNewSoftwareReleaseUrl()
software_type = 'public type'
software_product, _, _ = self.addSoftwareProduct(
"instance product", project, software_release_url, software_type
)
# Create supply to buy services
sale_supply = self.portal.sale_supply_module.newContent(
portal_type="Sale Supply",
title="price for %s" % project.getRelativeUrl(),
source_project_value=project,
price_currency_value=currency
)
sale_supply.newContent(
portal_type="Sale Supply Line",
base_price=6,
resource_value=software_product
)
sale_supply.newContent(
portal_type="Sale Supply Line",
base_price=7,
resource="service_module/slapos_compute_node_subscription"
)
sale_supply.validate()
self.tic()
# Ensure no unexpected object has been created
# 2 assignment
# 2 sale trade condition
# 1 subscription requests
# 1 software product
# 3 sale supply + lines
self.assertRelatedObjectCount(project, 9)
##################################################
# Add deposit
with PinnedDateTime(self, creation_date):
payment_transaction = owner_person.Person_addDepositPayment(99*100, currency.getRelativeUrl(), 1)
payment_transaction.PaymentTransaction_acceptDepositPayment()
##################################################
# Add first batch of service, and generate invoices
with PinnedDateTime(self, creation_date):
self.logout()
self.login(owner_person.getUserId())
self.portal.portal_slap.requestComputer(self.generateNewId(),
project.getReference())
self.portal.portal_slap.requestComputerPartition(
software_release=software_release_url,
software_type=software_type,
partition_reference=self.generateNewId(),
shared_xml='<marshal><bool>0</bool></marshal>',
project_reference=project.getReference())
self.logout()
self.login()
# Execute activities for all services
# To try detection bad activity tag dependencies
self.tic()
# Get object
instance_tree = self.portal.portal_catalog.getResultValue(
portal_type='Instance Tree',
follow_up__uid=project.getUid()
)
compute_node = self.portal.portal_catalog.getResultValue(
portal_type='Compute Node',
follow_up__uid=project.getUid()
)
##################################################
# Ensure new monthly invoices are created
# Regularisation Request must also be created
with PinnedDateTime(self, creation_date + 32):
self.logout()
self.login()
self.portal.portal_alarms.update_open_order_simulation.activeSense()
self.tic()
regularisation_request = self.portal.portal_catalog.getResultValue(
portal_type='Regularisation Request',
destination__uid=owner_person.getUid()
)
self.assertEquals(regularisation_request.getSimulationState(), 'suspended')
##################################################
# Trigger regularisation request escalation
self.logout()
self.login()
for date_offset in range(33, 70, 1):
# Trigger the alarm everyday, to not depend too much
# of the alarm crm delay current implementation
with PinnedDateTime(self, creation_date + date_offset):
for alarm_id in [
'slapos_crm_trigger_acknowledgment_escalation',
'slapos_crm_trigger_delete_reminder_escalation',
'update_open_order_simulation',
'slapos_stop_confirmed_aggregated_sale_invoice_transaction'
]:
self.portal.portal_alarms[alarm_id].activeSense()
self.tic()
##################################################
# Items must be deleted
# Open Order must be archived
# Invoice must be paid with Deposit
self.assertEquals(project.getValidationState(), 'invalidated')
self.assertEquals(instance_tree.getValidationState(), 'archived')
self.assertEquals(instance_tree.getSlapState(), 'destroy_requested')
self.assertEquals(compute_node.getValidationState(), 'invalidated')
open_order_list = self.portal.portal_catalog(
portal_type='Open Sale Order',
destination_section__uid=owner_person.getUid()
)
hosting_subscription_list = []
self.assertEquals(len(open_order_list), 3)
for open_order in open_order_list:
self.assertEquals(open_order.getValidationState(), 'archived')
self.assertNotEquals(open_order.getStopDate(), open_order.getStartDate())
self.assertNotEquals(open_order.getStopDate(), None)
self.assertEquals(open_order.getStopDate(), DateTime('2020/07/17'))
for line in open_order.contentValues():
for cell in line.contentValues():
hosting_subscription_list.append(
cell.getAggregateValue(portal_type='Hosting Subscription')
)
tmp = line.getAggregateValue(portal_type='Hosting Subscription')
if tmp is not None:
hosting_subscription_list.append(tmp)
self.assertEquals(len(hosting_subscription_list), 3)
for hosting_subscription in hosting_subscription_list:
self.assertEquals(hosting_subscription.getValidationState(), 'archived')
self.assertEquals(regularisation_request.getSimulationState(), 'suspended')
self.assertEquals(regularisation_request.getResourceId(),
'slapos_crm_delete_acknowledgement')
# No planned invoice is expected
ledger_uid = self.portal.portal_categories.ledger.automated.getUid()
outstanding_amount_list = owner_person.Entity_getOutstandingAmountList(
ledger_uid=ledger_uid
)
self.assertEquals(len(outstanding_amount_list), 1)
self.assertEquals(outstanding_amount_list[0].total_price, 132)
planned_outstanding_amount_list = owner_person.Entity_getOutstandingAmountList(
ledger_uid=ledger_uid,
include_planned=True
)
self.assertEquals(len(planned_outstanding_amount_list), 1)
self.assertEquals(outstanding_amount_list[0].total_price,
planned_outstanding_amount_list[0].total_price)
with PinnedDateTime(self, creation_date + 5):
self.checkERP5StateBeforeExit()
\ No newline at end of file
......@@ -8,7 +8,7 @@
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSCloudTicketSlapInterfaceWorkflow</string> </value>
<value> <string>testSlapOSERP5CRMScenario</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.testSlapOSCloudTicketSlapInterfaceWorkflow</string> </value>
<value> <string>test.erp5.testSlapOSERP5CRMScenario</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow" module="erp5.portal_type"/>
<global name="Interaction Workflow" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......@@ -24,14 +24,6 @@
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>source/portal_workflow/ticket_slap_interface_workflow/state_draft</string>
</tuple>
</value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
......@@ -40,7 +32,7 @@
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>person_slap_interface_workflow</string> </value>
<value> <string>slapos_accounting_interaction_workflow</string> </value>
</item>
<item>
<key> <string>description</string> </key>
......@@ -50,7 +42,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ticket_slap_interface_workflow</string> </value>
<value> <string>slapos_crm_interaction_workflow</string> </value>
</item>
<item>
<key> <string>language</string> </key>
......@@ -58,27 +50,13 @@
<none/>
</value>
</item>
<item>
<key> <string>manager_bypass</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow</string> </value>
</item>
<item>
<key> <string>state_variable</string> </key>
<value> <string>slap_state</string> </value>
<value> <string>Interaction Workflow</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Ticket Slap Interface Workflow</string> </value>
</item>
<item>
<key> <string>workflow_managed_permission</string> </key>
<value>
<tuple/>
</value>
<value> <string>SlapOS CRM Interaction Workflow</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -2,29 +2,15 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Transition" module="erp5.portal_type"/>
<global name="Interaction Workflow Interaction" 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>before_script/portal_workflow/ticket_slap_interface_workflow/script_Ticket_checkConsistency</string>
<string>after_script/portal_workflow/ticket_slap_interface_workflow/script_Ticket_notify</string>
<string>after_script/portal_workflow/slapos_crm_interaction_workflow/script_Base_triggerDeleteNotPaidServiceAlarm</string>
</tuple>
</value>
</item>
......@@ -35,32 +21,40 @@
</value>
</item>
<item>
<key> <string>guard_permission</string> </key>
<value>
<tuple/>
</value>
<key> <string>id</string> </key>
<value> <string>interaction_Item_invalidate</string> </value>
</item>
<item>
<key> <string>icon</string> </key>
<key> <string>portal_type_filter</string> </key>
<value>
<none/>
<tuple>
<string>Instance Tree</string>
<string>Compute Node</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_notify</string> </value>
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Transition</string> </value>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Notify</string> </value>
<key> <string>trigger_method_id</string> </key>
<value>
<tuple>
<string>invalidate</string>
<string>archive</string>
</tuple>
</value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
<key> <string>trigger_once_per_transaction</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
......
......@@ -2,32 +2,38 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
<global name="Interaction Workflow Interaction" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>0</int> </value>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>after_script/portal_workflow/slapos_crm_interaction_workflow/script_Base_triggerDeleteNotPaidServiceAlarm</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Provides access to workflow history</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_history</string> </value>
<value> <string>interaction_RegularisationRequest_setResource</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
<key> <string>portal_type_filter</string> </key>
<value>
<tuple>
<string>Regularisation Request</string>
</tuple>
</value>
</item>
<item>
<key> <string>status_included</string> </key>
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>0</int> </value>
</item>
<item>
......@@ -37,8 +43,16 @@
</value>
</item>
<item>
<key> <string>variable_default_expression</string> </key>
<value> <string>state_change/getHistory</string> </value>
<key> <string>trigger_method_id</string> </key>
<value>
<tuple>
<string>_setResource.*</string>
</tuple>
</value>
</item>
<item>
<key> <string>trigger_once_per_transaction</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
......
......@@ -2,61 +2,65 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Transition" module="erp5.portal_type"/>
<global name="Interaction Workflow Interaction" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<key> <string>categories</string> </key>
<value>
<none/>
<tuple>
<string>after_script/portal_workflow/slapos_crm_interaction_workflow/script_Base_triggerInvalidateSuspendedRegularisationRequestAlarm</string>
</tuple>
</value>
</item>
<item>
<key> <string>action_name</string> </key>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/workflow</string>
<string>before_script/portal_workflow/ticket_slap_interface_workflow/script_Ticket_checkConsistency</string>
<string>after_script/portal_workflow/ticket_slap_interface_workflow/script_Ticket_approveRegistration</string>
</tuple>
</value>
<key> <string>id</string> </key>
<value> <string>interaction_SaleInvoiceTransactionLine_setGroupingReference</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<key> <string>portal_type</string> </key>
<value> <string>Interaction Workflow Interaction</string> </value>
</item>
<item>
<key> <string>portal_type_filter</string> </key>
<value>
<none/>
<tuple>
<string>Sale Invoice Transaction Line</string>
</tuple>
</value>
</item>
<item>
<key> <string>guard_permission</string> </key>
<key> <string>portal_type_group_filter</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value>
<none/>
</value>
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_approve_registration</string> </value>
<key> <string>title</string> </key>
<value> <string>SaleInvoiceTransactionLine_setGroupingReference</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Transition</string> </value>
<key> <string>trigger_method_id</string> </key>
<value>
<tuple>
<string>_baseSetGroupingReference</string>
</tuple>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Approve Registration</string> </value>
<key> <string>trigger_once_per_transaction</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
......
......@@ -2,61 +2,65 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Transition" module="erp5.portal_type"/>
<global name="Interaction Workflow Interaction" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<key> <string>categories</string> </key>
<value>
<none/>
<tuple>
<string>after_script/portal_workflow/slapos_crm_interaction_workflow/script_Base_triggerCreateRegularisationRequestAlarm</string>
</tuple>
</value>
</item>
<item>
<key> <string>action_name</string> </key>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/workflow</string>
<string>before_script/portal_workflow/ticket_slap_interface_workflow/script_Ticket_checkConsistency</string>
<string>after_script/portal_workflow/ticket_slap_interface_workflow/script_Ticket_requestEvent</string>
</tuple>
</value>
<key> <string>id</string> </key>
<value> <string>interaction_SaleInvoiceTransaction_stop</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<key> <string>portal_type</string> </key>
<value> <string>Interaction Workflow Interaction</string> </value>
</item>
<item>
<key> <string>portal_type_filter</string> </key>
<value>
<none/>
<tuple>
<string>Sale Invoice Transaction</string>
</tuple>
</value>
</item>
<item>
<key> <string>guard_permission</string> </key>
<key> <string>portal_type_group_filter</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value>
<none/>
</value>
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_request_event</string> </value>
<key> <string>title</string> </key>
<value> <string>SaleInvoiceTransaction_stop</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Transition</string> </value>
<key> <string>trigger_method_id</string> </key>
<value>
<tuple>
<string>stop</string>
</tuple>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Request Event</string> </value>
<key> <string>trigger_once_per_transaction</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
......
return state_change['object'].Base_reindexAndSenseAlarm(['slapos_crm_create_regularisation_request'])
......@@ -60,7 +60,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_Ticket_checkConsistency</string> </value>
<value> <string>script_Base_triggerCreateRegularisationRequestAlarm</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......
......@@ -52,15 +52,6 @@
<key> <string>_params</string> </key>
<value> <string>state_change</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
......@@ -69,15 +60,13 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_Ticket_requestEvent</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Script</string> </value>
<value> <string>script_Base_triggerDeleteNotPaidServiceAlarm</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Ticket_requestEvent</string> </value>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
......
return state_change['object'].Base_reindexAndSenseAlarm(['slapos_crm_invalidate_suspended_regularisation_request'])
......@@ -52,14 +52,6 @@
<key> <string>_params</string> </key>
<value> <string>state_change</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>
......@@ -68,7 +60,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_Ticket_notify</string> </value>
<value> <string>script_Base_triggerInvalidateSuspendedRegularisationRequestAlarm</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......@@ -76,7 +68,9 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Ticket_notify</string> </value>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
......
ticket = state_change["object"]
from DateTime import DateTime
portal = context.getPortalObject()
if ticket.getSimulationState() != "draft":
return
if ticket.getReference() in [None, ""]:
raise ValueError("Reference is missing on the Ticket")
# Get the user id of the context owner.
local_role_list = ticket.get_local_roles()
for group, role_list in local_role_list:
if 'Owner' in role_list:
user_id = group
break
person = portal.portal_catalog.getResultValue(user_id=user_id)
if person is None:
# Value was created by super user, so there isn't a point on continue
return
# XXX unhardcode the trade condition, by adding a preference
if ticket.getSpecialise() != "sale_trade_condition_module/slapos_ticket_trade_condition":
return
trade_condition = portal.sale_trade_condition_module.slapos_ticket_trade_condition
ticket.edit(
source_section=trade_condition.getSourceSection(),
source_trade=trade_condition.getSourceSection(),
source=trade_condition.getSource())
ticket.setStartDate(DateTime())
ticket.requestEvent(
event_title=ticket.getTitle(),
event_content=ticket.getDescription(),
event_source=ticket.getDestinationDecision()
)
event_relative_url = context.REQUEST.get("event_relative_url")
ticket.setCausality(event_relative_url)
<?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>_proxy_roles</string> </key>
<value>
<tuple>
<string>Assignor</string>
<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_Ticket_approveRegistration</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Script</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Ticket_approveRegistration</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
ticket = state_change["object"]
from DateTime import DateTime
portal = ticket.getPortalObject()
# Get required arguments
kwargs = state_change.kwargs
# Required args
# Raise TypeError if all parameters are not provided
try:
message_title = kwargs['message_title']
message = kwargs['message']
except KeyError:
raise TypeError("Ticket_notify takes exactly 2 arguments")
resource = portal.service_module.slapos_crm_information.getRelativeUrl()
# create Web message if needed for this ticket
last_event = ticket.SupportRequest_getLastEvent(message_title)
if last_event:
# User has already been notified for this problem.
ticket.REQUEST.set("ticket_notified_item", last_event)
return
transactional_event = ticket.REQUEST.get("ticket_notified_item", None)
if transactional_event is not None:
if (transactional_event.getFollowUpUid() == ticket.getUid()) and \
(transactional_event.getTitle() == message_title):
ticket.REQUEST.set("ticket_notified_item", transactional_event)
return
template = portal.restrictedTraverse(
portal.portal_preferences.getPreferredWebMessageTemplate())
event = template.Base_createCloneDocument(batch_mode=1)
event.edit(
title=message_title,
text_content=message,
start_date = DateTime(),
resource = resource,
source=ticket.getDestinationDecision(),
destination=ticket.getSource(),
follow_up=ticket.getRelativeUrl(),
)
event.stop()
event.deliver()
ticket.serialize()
ticket.REQUEST.set("ticket_notified_item", event)
ticket = state_change["object"]
from DateTime import DateTime
portal = context.getPortalObject()
# Get required arguments
kwargs = state_change.kwargs
# Required args
# Raise TypeError if all parameters are not provided
try:
title = kwargs['event_title']
text_content = kwargs['event_content']
source = kwargs['event_source']
except KeyError:
raise TypeError, "Ticket_requestEvent takes at exactly 3 argument"
web_message = portal.event_module.newContent(
portal_type="Web Message",
start_date = DateTime(),
title=title,
text_content=text_content,
source=source,
content_type="text/plain",
destination=ticket.getSource(),
resource=ticket.getResource(),
follow_up=ticket.getRelativeUrl()
)
web_message.stop(comment="Submitted from the renderjs app")
if portal.portal_workflow.isTransitionPossible(ticket, "validate"):
ticket.validate(comment="See %s" % web_message.getRelativeUrl())
ticket.REQUEST.set("event_relative_url", web_message.getRelativeUrl())
<?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/ticket_slap_interface_workflow/transition_approve_registration</string>
<string>destination/portal_workflow/ticket_slap_interface_workflow/transition_notify</string>
<string>destination/portal_workflow/ticket_slap_interface_workflow/transition_request_event</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_draft</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow State</string> </value>
</item>
<item>
<key> <string>state_permission_role_list_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>state_type</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Draft</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>The last transition</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_action</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
</item>
<item>
<key> <string>status_included</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>variable_default_expression</string> </key>
<value> <string>transition/getReference|nothing</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>The name of the user who performed the last transition</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_actor</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
</item>
<item>
<key> <string>status_included</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>variable_default_expression</string> </key>
<value> <string>user/getUserName</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Comments about the last transition</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_comment</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
</item>
<item>
<key> <string>status_included</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>variable_default_expression</string> </key>
<value> <string>python:state_change.kwargs.get(\'comment\', \'\')</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Error message if validation failed</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_error_message</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
</item>
<item>
<key> <string>status_included</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>portal type (use as filter for worklists)</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_portal_type</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
</item>
<item>
<key> <string>status_included</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Variable" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>automatic_update</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Time of the last transition</string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variable_time</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Variable</string> </value>
</item>
<item>
<key> <string>status_included</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>variable_default_expression</string> </key>
<value> <string>state_change/getDateTime</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
erp5_crm
\ No newline at end of file
erp5_crm
slapos_accounting
\ No newline at end of file
......@@ -2,10 +2,9 @@ Compute Node | view_related_support_request
Incident Response Module | rss_view
Incident Response Module | view
Incident Response | view
Instance Tree | jump_to_related_upgrade_decision_line
Instance Tree | jump_to_related_upgrade_decision
Instance Tree | view_status
Instance Tree | view_ticket
Person | slapos_user_pending_ticket_report
Regularisation Request Module | rss_view
Regularisation Request Module | view
Regularisation Request | clone_ticket_and_event_list
......@@ -13,10 +12,8 @@ Regularisation Request | create_new_file
Regularisation Request | document_list
Regularisation Request | new_event
Regularisation Request | view
Site Message | update_destination_for_slapos
Support Request Module | rss_view
Support Request Module | slapos_support_request_instance_message_report
Support Request Module | support_request_monitor_link
Support Request Module | support_request_monitor_opml_description_list
Support Request | jump_related_slapos_item
Support Request | view_monitoring
\ No newline at end of file
portal_types/Support Request Module/6
sale_trade_condition_module/slapos_ticket_trade_condition
sale_trade_condition_module/slapos_ticket_trade_condition/**
service_module/slapos_crm_acknowledgement
service_module/slapos_crm_complaint
service_module/slapos_crm_information
service_module/slapos_crm_spam
service_module/slapos_crm_invoice_cancellation
service_module/slapos_crm_stop_reminder
service_module/slapos_crm_stop_acknowledgement
service_module/slapos_crm_delete_acknowledgement
service_module/slapos_crm_delete_reminder
service_module/slapos_crm_information
service_module/slapos_crm_invoice_cancellation
service_module/slapos_crm_monitoring
service_module/slapos_crm_upgrade
portal_types/Support Request Module/6
sale_trade_condition_module/slapos_ticket_trade_condition
sale_trade_condition_module/slapos_ticket_trade_condition/**
\ No newline at end of file
service_module/slapos_crm_spam
service_module/slapos_crm_stop_acknowledgement
service_module/slapos_crm_stop_reminder
service_module/slapos_crm_upgrade
\ No newline at end of file
service_module/slapos_crm_acknowledgement
service_module/slapos_crm_complaint
service_module/slapos_crm_information
service_module/slapos_crm_spam
service_module/slapos_crm_invoice_cancellation
service_module/slapos_crm_stop_reminder
service_module/slapos_crm_stop_acknowledgement
service_module/slapos_crm_delete_acknowledgement
service_module/slapos_crm_delete_reminder
service_module/slapos_crm_information
service_module/slapos_crm_invoice_cancellation
service_module/slapos_crm_monitoring
service_module/slapos_crm_upgrade
support_request_module/slapos_crm_support_request_template
regularisation_request_module/slapos_crm_regularisation_request_template
event_module/slapos_crm_web_message_template
sale_trade_condition_module/slapos_ticket_trade_condition
sale_trade_condition_module/slapos_ticket_trade_condition/**
\ No newline at end of file
service_module/slapos_crm_spam
service_module/slapos_crm_stop_acknowledgement
service_module/slapos_crm_stop_reminder
service_module/slapos_crm_upgrade
\ No newline at end of file
event_module/slapos_crm_web_message_template
person_module/allocation_tester
portal_alarms/slapos_crm_*
portal_categories/monitor_scope/**
portal_categories/upgrade_scope/**
regularisation_request_module/slapos_crm_regularisation_request_template
sale_trade_condition_module/slapos_ticket_trade_condition
sale_trade_condition_module/slapos_ticket_trade_condition/**
service_module/slapos_crm_*
support_request_module/slapos_crm_support_request_template
\ No newline at end of file
service_module/slapos_crm_*
\ No newline at end of file
Compute Node | monitor_scope
Compute Node | upgrade_scope
Incident Response Module | business_application
Incident Response | aggregate
Incident Response | specialise
Instance Tree | monitor_scope
Instance Tree | upgrade_scope
Regularisation Request Module | business_application
Regularisation Request | specialise
Support Request | aggregate
......
Mail Message | SlapOSEventConstraint
Regularisation Request | Codification
Site Message | SlapOSEventConstraint
Support Request | SlapOSSupportRequestConstraint
Web Message | SlapOSEventConstraint
\ No newline at end of file
Compute Node | slapos_crm_interaction_workflow
Incident Response | edit_workflow
Incident Response | ticket_workflow
Instance Tree | slapos_crm_interaction_workflow
Regularisation Request | edit_workflow
Regularisation Request | pricing_interaction_workflow
Regularisation Request | slapos_crm_interaction_workflow
Regularisation Request | ticket_interaction_workflow
Regularisation Request | ticket_slap_interface_workflow
Regularisation Request | ticket_workflow
Support Request | ticket_slap_interface_workflow
\ No newline at end of file
Sale Invoice Transaction Line | slapos_crm_interaction_workflow
Sale Invoice Transaction | slapos_crm_interaction_workflow
\ No newline at end of file
SlapOSCRMSystemPreference
SlapOSEventConstraint
SlapOSSupportRequestConstraint
\ No newline at end of file
SlapOSEventConstraint
\ No newline at end of file
test.erp5.testSlapOSCRMSkins
test.erp5.testSlapOSCRMRegularisationRequestSkins
test.erp5.testSlapOSCRMAlarm
test.erp5.testSlapOSCloudTicketSlapInterfaceWorkflow
\ No newline at end of file
test.erp5.testSlapOSCRMRegularisationRequestSkins
test.erp5.testSlapOSCRMSkins
test.erp5.testSlapOSERP5CRMScenario
\ No newline at end of file
ticket_slap_interface_workflow
slapos_crm_interaction_workflow
ticket_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