Commit ab483356 authored by Romain Courteaud's avatar Romain Courteaud

slapos_crm:

* source_administration is not used anymore on Compute Node
* add virtual master contraint to tickets and events portal types
* propagate project from Regularisation Request to event
* delivery/movement must use source_project instead of follow_up
* set source_project on event
* stop setting person as source_project
* copy project from ticket to the new event
* utf-8 encoding issue
* XXX disable regularisation request alarm
* Regularisation Request Module acquires local roles
  See other modules
* drop upgrade_scope
  Upgrade is managed by Allocation Supply.
* drip upgrade_scope usage
* drop Upgrade Decision Line usage
* lint test
* fix slapos_crm_check_software_installation_state alarm test
* fixup slapos_crm_check_compute_node_state tests
* fixup slapos_crm_check_instance_in_error test
* fixup slapos_crm_update_support_request_state tests
* fixup slapos_crm_send_pending_ticket_reminder test
* fixup slapos_crm_trigger_stop_reminder_escalation tests
* fixup slapos_crm_trigger_stop_acknowledgment_escalation test
* fixup slapos_crm_trigger_delete_reminder_escalation test
* fix slapos_crm_trigger_acknowledgment_escalation test
* fix slapos_crm_stop_instance_tree test
* fix slapos_crm_invalidate_suspended_regularisation_request test
* fix slapos_crm_delete_instance_tree test
* fix slapos_crm_create_regularisation_request test
* drop slapos_crm_check_update_allocation_scope tests
* do not require source_project on crm
* fixup rebase: drop Instance Tree Module | slapos_resilience_usage_report
* drop preferred_support_request_template_property
* drop slapos_crm_support_request_template
* fixup slapos_crm_create_regularisation_request tests
* reactivate slapos_crm_create_regularisation_request
* test slapos_crm_invalidate_suspended_regularisation_request is ok
* test slapos_crm_check_software_installation_state
* test slapos_crm_check_instance_in_error
* test slapos_crm_update_support_request_state
* desactivate Person_getSubscriptionRequestFirstUnpaidInvoiceList
* drop Person_getSubscriptionRequestFirstUnpaidInvoiceList
* do not count all Support Requests
  limit parameter with count is meaningless, as count return 1 result
* update Base_getSupportRequestInProgress usage
* XXX disable ticket.notify and ticket.requestEvent
* wip script to create Support Request and Event
* search a matching Trade Condition to generate a Support Request
* update ComputeNode_checkState to use new scripts
* test ComputeNode_checkSoftwareInstallationState
* deliver outgoing events
* test InstanceTree_checkSoftwareInstanceState
* Project_createSupportRequestWithCausality already checks if Support Request exists
* no need to check twice the object portal type
* test SupportRequest_updateMonitoringState
* activate one more alarm
* revert  Send Mail on pending tickets
  nexedi/slapos.core!417
  Virtual Master's manager are responsible to handle the tickets through their worklist.
  Not user.
  Virtual Master's manager can send email from the Ticket to directly ping a user.
* drop ticket_slap_interface_workflow
* test ComputeNode_hasContactedRecently
  Do not check if there is a packing list. It is unrelated to the contact.
* Base_getOpenRelatedTicketList was dropped
* ComputeNode_checkAndUpdateAllocationScope was dropped
* test: simplify
* set comment in the event workflow history
* send Mail Message
* unify event creation
  Reuse Ticket_createProjectEvent
* do not pass a empty list of node_uid
* no need to manually handle the notification message
* create the Regularisation Request from a Trade Condition instead of a template
* use notification message title instead of the ticket title
* use Entity_hasOutstandingAmount
  Entity_getOutstandingAmount is meaningless in case of multiple source_section and multiple currencies.
  Entity_hasOutstandingAmount will only report True or False.
* test RegularisationRequest_invalidateIfPersonBalanceIsOk
* test RegularisationRequest_checkToTriggerNextEscalationStep
* test RegularisationRequest_triggerStopReminderEscalation
* test RegularisationRequest_triggerStopAcknowledgmentEscalation
* test RegularisationRequest_triggerDeleteReminderEscalation
* test RegularisationRequest_stopInstanceTreeList
* test RegularisationRequest_deleteInstanceTreeList
* fixup RegularisationRequest_checkToSendUniqEvent tests
* fixup RegularisationRequest_checkToTriggerNextEscalationStep tests
* fixup InstanceTree_stopFromRegularisationRequest tests
* fixup InstanceTree_deleteFromRegularisationRequest tests
* drop slapos_crm_regularisation_request_template
* drop slapos_crm_web_message_template
* drop preferred_web_message_template_property
* drop slapos_ticket_trade_condition
* drop update_destination_for_slapos
* drop allocation_tester
* drop template_software_installation
* drop template_instance_tree
* drop template_member
* test: fixup person title
* test: fixup: ensure tickets can be created
* test: fixup creation of instance tree
* test: fixup instance tree creation
* test: ComputeNode_getTicketRelatedList was dropped
* test: rss feed will be used by virtual manager production
* test: ticket.approveRegistration was dropped
* ExactMatch
* drop preferred_cloud_contract_enabled_property
* only submit the newlu created Support Request
  end user can not validate their submitted ticket
* drop jump_related_slapos_item
* drop outdated SlapOSSupportRequestConstraint
* submit created event
  This allow to separate new ticket from the one currently handled
* constraint was dropped
* test: create test ticket in submitted state
* support request are created in submitted state
* drop open/personal allocation_scope
* only check automated Outstanding Invoices
* invalidate Regularisation Request as soon as payment is done
* create the regularisation request as soon as possible
* use _baseSetGroupingReference for interaction
* test: disable interaction
* test: use PinnedDateTime context
* delete instance, compute node, project if regularisation request reach the end
* test: add crm scenario showing services are destroyed
* trigger Project invalidation as soon as Nodes and Instances are invalidated
* trigger not paid item deletion as fast as possible
* test: reduce number of alarm triggered
* do not stop but delete directly
  Services were already in degraded mode due to allocation/propagation being forbidden
* test: only payable services are destroyed by the subscription request
parent f2e74d9f
......@@ -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,24 +3,19 @@ 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(
if person_uid_list:
portal.portal_catalog.searchAndActivate(
portal_type="Person",
validation_state="validated",
uid=person_uid_list,
......
......@@ -3,7 +3,7 @@ sub_tag = "RegularisationRequest_deleteInstanceTreeList"
portal.portal_catalog.searchAndActivate(
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
......
......@@ -3,7 +3,7 @@ sub_tag = "RegularisationRequest_stopInstanceTreeList"
portal.portal_catalog.searchAndActivate(
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(),
......
......@@ -2,7 +2,7 @@ portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
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}
)
......
......@@ -2,7 +2,7 @@ portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
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,29 +35,19 @@ 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,
......@@ -79,27 +57,17 @@ 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,7 +4,7 @@ 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']):
......@@ -13,7 +13,7 @@ if (state == 'suspended') and \
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
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
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_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 \
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):
return context.InstanceTree_createSupportRequestEvent(
instance, 'slapos-crm-instance-tree-instance-state.notification')
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:
has_unallocated_instance = True
failing_instance = instance
error_message = "No message!"
if has_unallocated_instance and has_newest_allocated_instance:
return context.InstanceTree_createSupportRequestEvent(
failing_instance, 'slapos-crm-instance-tree-instance-allocation.notification')
project = context.getFollowUpValue()
support_request = project.Project_createSupportRequestWithCausality(
ticket_title,
description,
causality=context.getRelativeUrl(),
destination_decision=context.getDestinationSection()
)
if support_request is None:
return
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
if document.getPortalType() == "Instance Tree":
if document.getSlapState() == "destroy_requested":
return context.SupportRequest_updateMonitoringDestroyRequestedState()
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()
}
)
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,7 +111,8 @@ 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()
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()
self._makeComputeNode()
self._makeComputePartitionList()
instance = self.portal.software_instance_module.newContent(
portal_type="Software Instance",
reference=self.generateNewId()
)
_, partition = self._makeComputeNode(self.addProject())
error_date = DateTime() - 0.1
with PinnedDateTime(self, error_date):
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,99 +393,13 @@ 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())
......@@ -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
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_spam
service_module/slapos_crm_stop_acknowledgement
service_module/slapos_crm_stop_reminder
service_module/slapos_crm_upgrade
\ 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/**
\ 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_spam
service_module/slapos_crm_stop_acknowledgement
service_module/slapos_crm_stop_reminder
service_module/slapos_crm_upgrade
\ No newline at end of file
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
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_*
\ No newline at end of file
support_request_module/slapos_crm_support_request_template
\ 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
\ No newline at end of file
SlapOSSupportRequestConstraint
\ 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