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

slapos_crm: support for the virtual master logic

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

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

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

Prevent allocation if a customer has an ongoing Regularisation Request.

TODO: handle case of unpaid invoices for professional customers

Also in this commit:
* drop templates
* use interaction workflow to trigger alarms
parent 286a3bcc
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <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>
<item> <item>
<key> <string>permissions</string> </key> <key> <string>permissions</string> </key>
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
</item> </item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
<value> <string>Related Upgrade Decision Line</string> </value> <value> <string>Related Upgrade Decision</string> </value>
</item> </item>
<item> <item>
<key> <string>visible</string> </key> <key> <string>visible</string> </key>
...@@ -81,7 +81,7 @@ ...@@ -81,7 +81,7 @@
<key> <string>text</string> </key> <key> <string>text</string> </key>
<value> <string encoding="cdata"><![CDATA[ <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> ]]></string> </value>
</item> </item>
...@@ -96,7 +96,7 @@ string:${object_url}/Base_jumpToRelatedObjectList?base_category=aggregate&portal ...@@ -96,7 +96,7 @@ string:${object_url}/Base_jumpToRelatedObjectList?base_category=aggregate&portal
<dictionary> <dictionary>
<item> <item>
<key> <string>text</string> </key> <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> </item>
</dictionary> </dictionary>
</pickle> </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> <module>
<category_list>
<category>business_application/crm</category>
</category_list>
<id>incident_response_module</id> <id>incident_response_module</id>
<permission_list> <permission_list>
<permission type='tuple'> <permission type='tuple'>
......
<?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 @@ ...@@ -10,13 +10,17 @@
<key> <string>active_sense_method_id</string> </key> <key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_checkComputeNodeState</string> </value> <value> <string>Alarm_checkComputeNodeState</string> </value>
</item> </item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </value>
</item>
<item> <item>
<key> <string>description</string> </key> <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> <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>
<item> <item>
<key> <string>enabled</string> </key> <key> <string>enabled</string> </key>
<value> <int>0</int> </value> <value> <int>1</int> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
...@@ -10,13 +10,17 @@ ...@@ -10,13 +10,17 @@
<key> <string>active_sense_method_id</string> </key> <key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_checkInstanceTreeState</string> </value> <value> <string>Alarm_checkInstanceTreeState</string> </value>
</item> </item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </value>
</item>
<item> <item>
<key> <string>description</string> </key> <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> <value> <string>Check and create a Ticket when an instance is partially allocated for more than 4 hours.</string> </value>
</item> </item>
<item> <item>
<key> <string>enabled</string> </key> <key> <string>enabled</string> </key>
<value> <int>0</int> </value> <value> <int>1</int> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
</item> </item>
<item> <item>
<key> <string>enabled</string> </key> <key> <string>enabled</string> </key>
<value> <int>0</int> </value> <value> <int>1</int> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <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="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>
<base_category_list> <base_category_list>
<portal_type id="Compute Node"> <portal_type id="Compute Node">
<item>monitor_scope</item> <item>monitor_scope</item>
<item>upgrade_scope</item>
</portal_type> </portal_type>
<portal_type id="Incident Response"> <portal_type id="Incident Response">
<item>aggregate</item> <item>aggregate</item>
...@@ -12,7 +11,6 @@ ...@@ -12,7 +11,6 @@
</portal_type> </portal_type>
<portal_type id="Instance Tree"> <portal_type id="Instance Tree">
<item>monitor_scope</item> <item>monitor_scope</item>
<item>upgrade_scope</item>
</portal_type> </portal_type>
<portal_type id="Regularisation Request"> <portal_type id="Regularisation Request">
<item>specialise</item> <item>specialise</item>
......
...@@ -8,9 +8,6 @@ ...@@ -8,9 +8,6 @@
<portal_type id="Site Message"> <portal_type id="Site Message">
<item>SlapOSEventConstraint</item> <item>SlapOSEventConstraint</item>
</portal_type> </portal_type>
<portal_type id="Support Request">
<item>SlapOSSupportRequestConstraint</item>
</portal_type>
<portal_type id="Web Message"> <portal_type id="Web Message">
<item>SlapOSEventConstraint</item> <item>SlapOSEventConstraint</item>
</portal_type> </portal_type>
......
...@@ -12,10 +12,20 @@ ...@@ -12,10 +12,20 @@
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value> </value>
</item> </item>
<item>
<key> <string>acquire_local_roles</string> </key>
<value> <int>1</int> </value>
</item>
<item> <item>
<key> <string>content_icon</string> </key> <key> <string>content_icon</string> </key>
<value> <string>folder_icon.gif</string> </value> <value> <string>folder_icon.gif</string> </value>
</item> </item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item> <item>
<key> <string>factory</string> </key> <key> <string>factory</string> </key>
<value> <string>addFolder</string> </value> <value> <string>addFolder</string> </value>
...@@ -32,6 +42,18 @@ ...@@ -32,6 +42,18 @@
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Regularisation Request Module</string> </value> <value> <string>Regularisation Request Module</string> </value>
</item> </item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item> <item>
<key> <string>portal_type</string> </key> <key> <string>portal_type</string> </key>
<value> <string>Base Type</string> </value> <value> <string>Base Type</string> </value>
...@@ -40,6 +62,12 @@ ...@@ -40,6 +62,12 @@
<key> <string>type_class</string> </key> <key> <string>type_class</string> </key>
<value> <string>Folder</string> </value> <value> <string>Folder</string> </value>
</item> </item>
<item>
<key> <string>type_interface</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
......
<workflow_chain> <workflow_chain>
<chain>
<type>Compute Node</type>
<workflow>slapos_crm_interaction_workflow</workflow>
</chain>
<chain> <chain>
<type>Incident Response</type> <type>Incident Response</type>
<workflow>edit_workflow, ticket_workflow</workflow> <workflow>edit_workflow, ticket_workflow</workflow>
</chain> </chain>
<chain>
<type>Instance Tree</type>
<workflow>slapos_crm_interaction_workflow</workflow>
</chain>
<chain> <chain>
<type>Regularisation Request</type> <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>
<chain> <chain>
<type>Support Request</type> <type>Sale Invoice Transaction Line</type>
<workflow>ticket_slap_interface_workflow</workflow> <workflow>slapos_crm_interaction_workflow</workflow>
</chain> </chain>
</workflow_chain> </workflow_chain>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/boolean</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_cloud_contract_enabled_property</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: False</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/string</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_regularisation_request_template_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: \'\'</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/string</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_support_request_template_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: \'\'</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/string</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_web_message_template_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: \'\'</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Property Sheet" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SlapOSSupportRequestConstraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Property Sheet</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Script Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>constraint_type/default</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>causality_source_destination_constraint_constraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Script Constraint</string> </value>
</item>
<item>
<key> <string>script_id</string> </key>
<value> <string>SupportRequest_checkCausalitySourceDestinationConsistency</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -3,28 +3,23 @@ portal = context.getPortalObject() ...@@ -3,28 +3,23 @@ portal = context.getPortalObject()
person_uid_list = [] person_uid_list = []
for (_, brain) in enumerate(portal.portal_simulation.getInventoryList( for (_, brain) in enumerate(portal.portal_simulation.getInventoryList(
simulation_state=('stopped', 'delivered'), 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, group_by_mirror_section=True,
portal_type=portal.getPortalAccountingMovementTypeList(), portal_type=portal.getPortalAccountingMovementTypeList(),
node_uid=[x.uid for x in context.Base_getReceivableAccountList()], node_uid=[x.uid for x in context.Base_getReceivableAccountList()] or -1,
grouping_reference=None)): parent__ledger__uid=portal.portal_categories.ledger.automated.getUid(),
grouping_reference=None
)):
payment_request_uid = brain.payment_request_uid section_uid = brain.getDestinationSectionUid(portal_type="Person")
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")
if section_uid is not None: if section_uid is not None:
person_uid_list.append(section_uid) person_uid_list.append(section_uid)
portal.portal_catalog.searchAndActivate( if person_uid_list:
portal_type="Person", portal.portal_catalog.searchAndActivate(
validation_state="validated", portal_type="Person",
uid=person_uid_list, validation_state="validated",
method_id='Person_checkToCreateRegularisationRequest', uid=person_uid_list,
activate_kw={'tag': tag} method_id='Person_checkToCreateRegularisationRequest',
) activate_kw={'tag': tag}
)
context.activate(after_tag=tag).getId() context.activate(after_tag=tag).getId()
portal = context.getPortalObject() portal = context.getPortalObject()
sub_tag = "RegularisationRequest_deleteInstanceTreeList" sub_tag = "RegularisationRequest_deleteInstanceTreeList"
portal.portal_catalog.searchAndActivate( portal.portal_catalog.searchAndActivate(
portal_type="Regularisation Request", portal_type="Regularisation Request",
simulation_state=["suspended"], 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_id='RegularisationRequest_deleteInstanceTreeList',
method_args=(sub_tag,), method_args=(sub_tag,),
# Limit activity number, as method_id also calls searchAndActivate # Limit activity number, as method_id also calls searchAndActivate
......
portal = context.getPortalObject() portal = context.getPortalObject()
sub_tag = "RegularisationRequest_stopInstanceTreeList" sub_tag = "RegularisationRequest_stopInstanceTreeList"
portal.portal_catalog.searchAndActivate( portal.portal_catalog.searchAndActivate(
portal_type="Regularisation Request", portal_type="Regularisation Request",
simulation_state=["suspended"], simulation_state=["suspended"],
default_resource_uid=[ resource__uid=[
portal.service_module.slapos_crm_stop_acknowledgement.getUid(), portal.service_module.slapos_crm_stop_acknowledgement.getUid(),
portal.service_module.slapos_crm_delete_reminder.getUid(), portal.service_module.slapos_crm_delete_reminder.getUid(),
portal.service_module.slapos_crm_delete_acknowledgement.getUid(), portal.service_module.slapos_crm_delete_acknowledgement.getUid(),
......
portal = context.getPortalObject() portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate( portal.portal_catalog.searchAndActivate(
portal_type="Regularisation Request", portal_type="Regularisation Request",
simulation_state=["suspended"], 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', method_id='RegularisationRequest_triggerAcknowledgmentEscalation',
activate_kw={'tag': tag} activate_kw={'tag': tag}
) )
......
portal = context.getPortalObject() portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate( portal.portal_catalog.searchAndActivate(
portal_type="Regularisation Request", portal_type="Regularisation Request",
simulation_state=["suspended"], 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', method_id='RegularisationRequest_triggerStopReminderEscalation',
activate_kw={'tag': tag} activate_kw={'tag': tag}
) )
......
...@@ -15,7 +15,8 @@ if (slap_state in ['start_requested', 'stop_requested']): ...@@ -15,7 +15,8 @@ if (slap_state in ['start_requested', 'stop_requested']):
software_type=instance_tree.getSourceReference(), software_type=instance_tree.getSourceReference(),
instance_xml=instance_tree.getTextContent(), instance_xml=instance_tree.getTextContent(),
sla_xml=instance_tree.getSlaXml(), sla_xml=instance_tree.getSlaXml(),
shared=instance_tree.isRootSlave() shared=instance_tree.isRootSlave(),
project_reference=instance_tree.getFollowUpReference()
) )
return True return True
return False return False
...@@ -15,7 +15,8 @@ if (slap_state == 'start_requested'): ...@@ -15,7 +15,8 @@ if (slap_state == 'start_requested'):
software_type=instance_tree.getSourceReference(), software_type=instance_tree.getSourceReference(),
instance_xml=instance_tree.getTextContent(), instance_xml=instance_tree.getTextContent(),
sla_xml=instance_tree.getSlaXml(), sla_xml=instance_tree.getSlaXml(),
shared=instance_tree.isRootSlave() shared=instance_tree.isRootSlave(),
project_reference=instance_tree.getFollowUpReference()
) )
return True return True
return False return False
...@@ -16,27 +16,15 @@ ticket_portal_type = "Regularisation Request" ...@@ -16,27 +16,15 @@ ticket_portal_type = "Regularisation Request"
ticket = portal.portal_catalog.getResultValue( ticket = portal.portal_catalog.getResultValue(
portal_type=ticket_portal_type, portal_type=ticket_portal_type,
default_source_project_uid=person.getUid(), destination__uid=person.getUid(),
simulation_state=['suspended', 'validated'], simulation_state=['suspended', 'validated'],
) )
if ticket is not None: if ticket is not None:
return ticket, None return ticket, None
outstanding_amount = person.Entity_statSlapOSOutstandingAmount() mail_message = None
if person.Entity_hasOutstandingAmount(ledger_uid=portal.portal_categories.ledger.automated.getUid()):
# 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:
tag = "%s_addRegularisationRequest_inProgress" % person.getUid() tag = "%s_addRegularisationRequest_inProgress" % person.getUid()
if (portal.portal_activities.countMessageWithTag(tag) > 0): if (portal.portal_activities.countMessageWithTag(tag) > 0):
# The regularisation request is already under creation but can not be fetched from catalog # The regularisation request is already under creation but can not be fetched from catalog
...@@ -47,59 +35,39 @@ if int(outstanding_amount) > 0: ...@@ -47,59 +35,39 @@ if int(outstanding_amount) > 0:
person.serialize() person.serialize()
# Time to create the ticket # Time to create the ticket
regularisation_request_template = portal.restrictedTraverse( comment = 'New automatic ticket for %s' % context.getTitle()
portal.portal_preferences.getPreferredRegularisationRequestTemplate()) ticket = context.Entity_createTicketFromTradeCondition(
ticket = regularisation_request_template.Base_createCloneDocument(batch_mode=1) portal.service_module.slapos_crm_monitoring.getRelativeUrl(),
ticket.edit( 'Account regularisation expected for "%s"' % context.getTitle(),
source_project_value=context, '',
title='Account regularisation expected for "%s"' % context.getTitle(), portal_type='Regularisation Request',
destination_decision_value=context, comment=comment
destination_value=context,
start_date=DateTime(),
resource=portal.portal_preferences.getPreferredRegularisationRequestResource(),
) )
ticket.validate(comment='New automatic ticket for %s' % context.getTitle()) ticket.validate(comment=comment)
ticket.suspend(comment='New automatic ticket for %s' % context.getTitle()) ticket.suspend(comment=comment)
ticket.reindexObject(activate_kw={'tag': tag}) ticket.reindexObject(activate_kw={'tag': tag})
# Notify using user's language subject = 'Invoice payment requested'
language = context.getLanguage("en") body = """Dear %s,
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,
A new invoice has been generated. A new invoice has been generated.
You can access it in your invoice section at %s. You can access it in your invoice section at %s.
Regards, Regards,
The slapos team The slapos team
""" % (context.getTitle(), portal.portal_preferences.getPreferredSlaposWebSiteUrl()) """ % (context.getTitle(), portal.portal_preferences.getPreferredSlaposWebSiteUrl())
notification_message_reference = "slapos-crm.create.regularisation.request"
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})
mail_message = ticket.RegularisationRequest_checkToSendUniqEvent( mail_message = ticket.RegularisationRequest_checkToSendUniqEvent(
portal.portal_preferences.getPreferredRegularisationRequestResource(), portal.portal_preferences.getPreferredRegularisationRequestResource(),
subject, subject,
body, body,
'Requested manual payment.') 'Requested manual payment.',
notification_message=notification_message_reference,
return ticket, mail_message 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" ...@@ -18,8 +18,8 @@ event_portal_type = "Mail Message"
event = portal.portal_catalog.getResultValue( event = portal.portal_catalog.getResultValue(
portal_type=event_portal_type, portal_type=event_portal_type,
default_resource_uid=service.getUid(), resource__uid=service.getUid(),
default_follow_up_uid=ticket.getUid(), follow_up__uid=ticket.getUid(),
) )
if (event is None) and (ticket.getSimulationState() == 'suspended'): if (event is None) and (ticket.getSimulationState() == 'suspended'):
...@@ -31,20 +31,15 @@ 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 # Prevent concurrent transaction to create 2 events for the same ticket
ticket.edit(resource=service_relative_url) ticket.edit(resource=service_relative_url)
event = portal.event_module.newContent( event = ticket.Ticket_createProjectEvent(
portal_type=event_portal_type, title, 'outgoing', 'Mail Message',
start_date=DateTime(), service_relative_url,
destination=ticket.getDestination(),
follow_up=ticket.getRelativeUrl(),
source=context.getSource(),
title=title,
resource=service_relative_url,
text_content=text_content, 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}) event.reindexObject(activate_kw={'tag': tag})
return event return event
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <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>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
...@@ -11,14 +11,16 @@ event_portal_type = "Mail Message" ...@@ -11,14 +11,16 @@ event_portal_type = "Mail Message"
event = portal.portal_catalog.getResultValue( event = portal.portal_catalog.getResultValue(
portal_type=event_portal_type, portal_type=event_portal_type,
default_resource_uid=current_service.getUid(), resource__uid=current_service.getUid(),
default_follow_up_uid=ticket.getUid(), follow_up__uid=ticket.getUid(),
simulation_state="delivered", simulation_state="delivered",
) )
if (ticket.getSimulationState() == 'suspended') and (event is not None) and (ticket.getResource() == current_service_relative_url): if (ticket.getSimulationState() == 'suspended') and (event is not None) and (ticket.getResource() == current_service_relative_url):
if (DateTime() - event.getStartDate()) > delay_period_in_days: 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 event.getRelativeUrl()
return None return None
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <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>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
...@@ -4,19 +4,66 @@ if REQUEST is not None: ...@@ -4,19 +4,66 @@ if REQUEST is not None:
ticket = context ticket = context
state = ticket.getSimulationState() state = ticket.getSimulationState()
person = ticket.getSourceProjectValue(portal_type="Person") person = ticket.getDestinationDecisionValue(portal_type="Person")
if (state == 'suspended') and \ if (state == 'suspended') and \
(person is not None) and \ (person is not None) and \
(ticket.getResource() == 'service_module/slapos_crm_delete_acknowledgement'): (ticket.getResource() == 'service_module/slapos_crm_delete_acknowledgement'):
portal = context.getPortalObject() portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate( subscribed_item_list = []
portal_type="Instance Tree",
validation_state=["validated"], ledger_uid = portal.portal_categories.ledger.automated.getUid()
default_destination_section_uid=person.getUid(), # Gather the list of not paid services
method_id='InstanceTree_deleteFromRegularisationRequest', for outstanding_amount in person.Entity_getOutstandingAmountList(
method_args=(person.getRelativeUrl(),), ledger_uid=ledger_uid,
activate_kw={'tag': tag} 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 True
return False return False
...@@ -3,26 +3,10 @@ if REQUEST is not None: ...@@ -3,26 +3,10 @@ if REQUEST is not None:
raise Unauthorized raise Unauthorized
state = context.getSimulationState() state = context.getSimulationState()
person = context.getSourceProjectValue(portal_type="Person") person = context.getDestinationDecisionValue(portal_type="Person")
if (state not in ('suspended', 'validated')) or \ if (state not in ('suspended', 'validated')) or \
(person is None): (person is None):
return return
outstanding_amount = person.Entity_statSlapOSOutstandingAmount() if not person.Entity_hasOutstandingAmount(ledger_uid=context.getPortalObject().portal_categories.ledger.automated.getUid()):
context.invalidate(comment="Automatically disabled as balance is ok")
# 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)
...@@ -4,16 +4,16 @@ if REQUEST is not None: ...@@ -4,16 +4,16 @@ if REQUEST is not None:
ticket = context ticket = context
state = ticket.getSimulationState() state = ticket.getSimulationState()
person = ticket.getSourceProjectValue(portal_type="Person") person = ticket.getDestinationDecisionValue(portal_type="Person")
if (state == 'suspended') and \ if (state == 'suspended') and \
(person is not None) 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']): (ticket.getResource() in ['service_module/slapos_crm_stop_acknowledgement', 'service_module/slapos_crm_delete_reminder', 'service_module/slapos_crm_delete_acknowledgement']):
portal = context.getPortalObject() portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate( portal.portal_catalog.searchAndActivate(
portal_type="Instance Tree", portal_type="Instance Tree",
validation_state=["validated"], validation_state=["validated"],
default_destination_section_uid=person.getUid(), destination_section__uid=person.getUid(),
method_id='InstanceTree_stopFromRegularisationRequest', method_id='InstanceTree_stopFromRegularisationRequest',
method_args=(person.getRelativeUrl(),), method_args=(person.getRelativeUrl(),),
activate_kw={'tag': tag} activate_kw={'tag': tag}
......
...@@ -2,19 +2,10 @@ from zExceptions import Unauthorized ...@@ -2,19 +2,10 @@ from zExceptions import Unauthorized
if REQUEST is not None: if REQUEST is not None:
raise Unauthorized raise Unauthorized
portal = context.getPortalObject()
ndays = 15 ndays = 15
language = "en"
recipient = context.getDestinationSectionValue()
if recipient is not None:
language = recipient.getLanguage("en")
notification_message = portal.portal_notifications.getDocumentValue( subject = 'Reminder: invoice payment requested'
language=language, reference="slapos-crm.acknowledgment.escalation") body = """Dear user,
if notification_message is None:
subject = 'Reminder: invoice payment requested'
body = """Dear user,
We would like to remind you the unpaid invoice you have on %s. 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. 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, ...@@ -23,26 +14,16 @@ Regards,
The slapos team The slapos team
""" % context.getPortalObject().portal_preferences.getPreferredSlaposWebSiteUrl() """ % 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( return context.RegularisationRequest_checkToTriggerNextEscalationStep(
delay_period_in_days=ndays, delay_period_in_days=ndays,
current_service_relative_url='service_module/slapos_crm_acknowledgement', 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, title=subject,
text_content=body, text_content=body,
comment='Stopping reminder.', 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 ...@@ -2,19 +2,10 @@ from zExceptions import Unauthorized
if REQUEST is not None: if REQUEST is not None:
raise Unauthorized raise Unauthorized
portal = context.getPortalObject()
ndays = 10 ndays = 10
language = "en"
recipient = context.getDestinationSectionValue()
if recipient is not None:
language = recipient.getLanguage("en")
notification_message = portal.portal_notifications.getDocumentValue( subject = 'Acknowledgment: instances deleted'
language=language, reference="slapos-crm.delete.reminder.escalation") body = """Dear user,
if notification_message is None:
subject = 'Acknowledgment: instances deleted'
body = """Dear user,
Despite our last reminder, you still have an unpaid invoice on %s. Despite our last reminder, you still have an unpaid invoice on %s.
We will now delete all your instances. We will now delete all your instances.
...@@ -22,20 +13,6 @@ We will now delete all your instances. ...@@ -22,20 +13,6 @@ We will now delete all your instances.
Regards, Regards,
The slapos team The slapos team
""" % context.getPortalObject().portal_preferences.getPreferredSlaposWebSiteUrl() """ % 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( return context.RegularisationRequest_checkToTriggerNextEscalationStep(
delay_period_in_days=ndays, delay_period_in_days=ndays,
...@@ -44,4 +21,9 @@ return context.RegularisationRequest_checkToTriggerNextEscalationStep( ...@@ -44,4 +21,9 @@ return context.RegularisationRequest_checkToTriggerNextEscalationStep(
title=subject, title=subject,
text_content=body, text_content=body,
comment='Deleting acknowledgment.', 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 ...@@ -2,19 +2,9 @@ from zExceptions import Unauthorized
if REQUEST is not None: if REQUEST is not None:
raise Unauthorized raise Unauthorized
portal = context.getPortalObject()
ndays = 7 ndays = 7
language = "en" subject = 'Last reminder: invoice payment requested'
recipient = context.getDestinationSectionValue() body = """Dear user,
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,
We would like to remind you the unpaid invoice you have on %s. 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. 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. ...@@ -22,20 +12,6 @@ If no payment is done during the coming days, we will delete all your instances.
Regards, Regards,
The slapos team The slapos team
""" % context.getPortalObject().portal_preferences.getPreferredSlaposWebSiteUrl() """ % 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( return context.RegularisationRequest_checkToTriggerNextEscalationStep(
delay_period_in_days=ndays, delay_period_in_days=ndays,
...@@ -44,4 +20,9 @@ return context.RegularisationRequest_checkToTriggerNextEscalationStep( ...@@ -44,4 +20,9 @@ return context.RegularisationRequest_checkToTriggerNextEscalationStep(
title=subject, title=subject,
text_content=body, text_content=body,
comment='Deleting reminder.', 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 ...@@ -2,19 +2,10 @@ from zExceptions import Unauthorized
if REQUEST is not None: if REQUEST is not None:
raise Unauthorized raise Unauthorized
portal = context.getPortalObject()
ndays = 7 ndays = 7
language = "en"
recipient = context.getDestinationSectionValue()
if recipient is not None:
language = recipient.getLanguage("en")
notification_message = portal.portal_notifications.getDocumentValue( subject = 'Acknowledgment: instances stopped'
language=language, reference="slapos-crm.stop.reminder.escalation") body = """Dear user,
if notification_message is None:
subject = 'Acknowledgment: instances stopped'
body = """Dear user,
Despite our last reminder, you still have an unpaid invoice on %s. 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. 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. ...@@ -22,20 +13,6 @@ We will now stop all your current instances to free some hardware resources.
Regards, Regards,
The slapos team The slapos team
""" % context.getPortalObject().portal_preferences.getPreferredSlaposWebSiteUrl() """ % 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( return context.RegularisationRequest_checkToTriggerNextEscalationStep(
delay_period_in_days=ndays, delay_period_in_days=ndays,
...@@ -44,4 +21,9 @@ return context.RegularisationRequest_checkToTriggerNextEscalationStep( ...@@ -44,4 +21,9 @@ return context.RegularisationRequest_checkToTriggerNextEscalationStep(
title=subject, title=subject,
text_content=body, text_content=body,
comment='Stopping acknowledgment.', 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( ...@@ -9,11 +9,11 @@ monitor_enabled_category = portal.restrictedTraverse(
if monitor_enabled_category is not None: if monitor_enabled_category is not None:
portal.portal_catalog.searchAndActivate( portal.portal_catalog.searchAndActivate(
portal_type = 'Compute Node', portal_type='Compute Node',
validation_state = 'validated', validation_state='validated',
default_monitor_scope_uid = monitor_enabled_category.getUid(), monitor_scope__uid=monitor_enabled_category.getUid(),
method_id = 'ComputeNode_checkState', method_id='ComputeNode_checkState',
activate_kw = {'tag':tag} activate_kw={'tag':tag}
) )
context.activate(after_tag=tag).getId() context.activate(after_tag=tag).getId()
...@@ -9,11 +9,11 @@ monitor_enabled_category = portal.restrictedTraverse( ...@@ -9,11 +9,11 @@ monitor_enabled_category = portal.restrictedTraverse(
if monitor_enabled_category is not None: if monitor_enabled_category is not None:
portal.portal_catalog.searchAndActivate( portal.portal_catalog.searchAndActivate(
portal_type = 'Compute Node', portal_type='Compute Node',
validation_state = 'validated', validation_state='validated',
default_monitor_scope_uid = monitor_enabled_category.getUid(), monitor_scope__uid=monitor_enabled_category.getUid(),
method_id = 'ComputeNode_checkSoftwareInstallationState', method_id='ComputeNode_checkSoftwareInstallationState',
activate_kw = {'tag':tag} activate_kw={'tag':tag}
) )
context.activate(after_tag=tag).getId() 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() ...@@ -3,11 +3,11 @@ portal = context.getPortalObject()
default_resource_uid = portal.restrictedTraverse("service_module/slapos_crm_monitoring", None).getUid() default_resource_uid = portal.restrictedTraverse("service_module/slapos_crm_monitoring", None).getUid()
portal.portal_catalog.searchAndActivate( portal.portal_catalog.searchAndActivate(
portal_type='Support Request', portal_type='Support Request',
simulation_state=['validated', 'suspended'], simulation_state=['submitted', 'validated', 'suspended'],
default_resource_uid=default_resource_uid, resource__uid=default_resource_uid,
default_aggregate_portal_type=["Instance Tree"], aggregate__portal_type=["Instance Tree"],
method_id='SupportRequest_updateMonitoringState', method_id='SupportRequest_updateMonitoringState',
activate_kw = {'tag':tag} activate_kw={'tag':tag}
) )
context.activate(after_tag=tag).getId() context.activate(after_tag=tag).getId()
from DateTime import DateTime from DateTime import DateTime
portal = context.getPortalObject() portal = context.getPortalObject()
person = context.getSourceAdministrationValue(portal_type="Person") if (context.getMonitorScope() == "disabled") or \
if not person or \
context.getMonitorScope() == "disabled" or \
portal.ERP5Site_isSupportRequestCreationClosed(): portal.ERP5Site_isSupportRequestCreationClosed():
return return
software_installation_list = portal.portal_catalog( software_installation_list = portal.portal_catalog(
portal_type='Software Installation', portal_type='Software Installation',
default_aggregate_uid=context.getUid(), aggregate__uid=context.getUid(),
validation_state='validated', validation_state='validated',
sort_on=(('creation_date', 'DESC'),) sort_on=(('creation_date', 'DESC'),)
) )
...@@ -26,15 +24,20 @@ for software_installation in software_installation_list: ...@@ -26,15 +24,20 @@ for software_installation in software_installation_list:
# Give it 12 hours to deploy. # Give it 12 hours to deploy.
continue continue
if software_installation.getSlapState() != 'start_requested':
continue
reference = software_installation.getReference() reference = software_installation.getReference()
d = software_installation.getAccessStatus() d = software_installation.getAccessStatus()
if d.get("no_data", None) == 1: 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) 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" % \ description = "The software release %s did not started to build on %s since %s" % \
(software_installation.getUrlString(), compute_node_title, software_installation.getCreationDate()) (software_installation.getUrlString(), compute_node_title, software_installation.getCreationDate())
else: else:
last_contact = DateTime(d.get('created_at')) last_contact = DateTime(d.get('created_at'))
if d.get("text").startswith("building"): if d.get("text").startswith("#building"):
should_notify = True should_notify = True
ticket_title = "[MONITORING] %s is building for too long on %s" % (reference, compute_node_reference) 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" % \ 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: ...@@ -49,42 +52,34 @@ for software_installation in software_installation_list:
(software_installation.getUrlString(), compute_node_title, software_installation.getCreationDate()) (software_installation.getUrlString(), compute_node_title, software_installation.getCreationDate())
if should_notify: 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") project = context.getFollowUpValue()
if support_request_relative_url is None: support_request = project.Project_createSupportRequestWithCausality(
return ticket_title,
description,
causality=context.getRelativeUrl(),
destination_decision=project.getDestination()
)
support_request = portal.restrictedTraverse(support_request_relative_url)
if support_request is None: if support_request is None:
return return
# Send Notification message notification_message_reference = 'slapos-crm-compute_node_software_installation_state.notification'
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})
event = support_request.SupportRequest_getLastEvent(ticket_title) event = support_request.SupportRequest_getLastEvent(ticket_title)
if event is None: 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) support_request_list.append(support_request)
......
from DateTime import DateTime from DateTime import DateTime
portal = context.getPortalObject() portal = context.getPortalObject()
person = context.getSourceAdministrationValue(portal_type="Person") if (context.getMonitorScope() == "disabled") or \
if not person or \
context.getMonitorScope() == "disabled" or \
portal.ERP5Site_isSupportRequestCreationClosed(): portal.ERP5Site_isSupportRequestCreationClosed():
return return
...@@ -56,7 +54,7 @@ if not should_notify: ...@@ -56,7 +54,7 @@ if not should_notify:
if compute_partition_uid_list: if compute_partition_uid_list:
instance_list = portal.portal_catalog( instance_list = portal.portal_catalog(
portal_type='Software Instance', portal_type='Software Instance',
default_aggregate_uid=compute_partition_uid_list) aggregate__uid=compute_partition_uid_list)
if instance_list: if instance_list:
should_notify = True should_notify = True
...@@ -78,45 +76,36 @@ if not should_notify: ...@@ -78,45 +76,36 @@ if not should_notify:
context.getTitle(), context.getReference(), last_contact) context.getTitle(), context.getReference(), last_contact)
if should_notify: if should_notify:
support_request = person.Base_getSupportRequestInProgress( support_request = context.Base_getSupportRequestInProgress(
title=node_ticket_title, title=node_ticket_title)
aggregate=context.getRelativeUrl())
if support_request is None: if support_request is None:
support_request = person.Base_getSupportRequestInProgress( project = context.getFollowUpValue()
title=ticket_title, support_request = project.Project_createSupportRequestWithCausality(
aggregate=context.getRelativeUrl()) ticket_title,
description,
if support_request is None: causality=context.getRelativeUrl(),
person.notify(support_request_title=ticket_title, destination_decision=project.getDestination()
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: if support_request is None:
return 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) event = support_request.SupportRequest_getLastEvent(ticket_title)
if event is None: 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 return support_request
portal = context.getPortalObject()
compute_node = context compute_node = context
now_date = DateTime() now_date = DateTime()
...@@ -15,13 +14,4 @@ if message_dict.has_key('created_at'): ...@@ -15,13 +14,4 @@ if message_dict.has_key('created_at'):
contact_date = DateTime(message_dict.get('created_at').encode('utf-8')) contact_date = DateTime(message_dict.get('created_at').encode('utf-8'))
return (now_date - contact_date) < maximum_days 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 return False
...@@ -96,8 +96,8 @@ ...@@ -96,8 +96,8 @@
<key> <string>left</string> </key> <key> <string>left</string> </key>
<value> <value>
<list> <list>
<string>my_reference</string>
<string>my_title</string> <string>my_title</string>
<string>my_reference</string>
</list> </list>
</value> </value>
</item> </item>
......
...@@ -2,19 +2,20 @@ from Products.ERP5Type.Cache import CachingMethod ...@@ -2,19 +2,20 @@ from Products.ERP5Type.Cache import CachingMethod
portal = context.getPortalObject() portal = context.getPortalObject()
def isSupportRequestCreationClosed(destination_decision=None): def isSupportRequestCreationClosed(destination_decision=None):
limit = portal.portal_preferences.getPreferredSupportRequestCreationLimit(5) limit = int(portal.portal_preferences.getPreferredSupportRequestCreationLimit(5))
kw = {} kw = {
kw['limit'] = limit 'limit': limit,
kw['portal_type'] = 'Support Request' 'portal_type': 'Support Request',
kw['simulation_state'] = ["validated","submitted"] 'simulation_state': ["validated", "submitted"],
kw['default_resource_uid'] = portal.service_module.slapos_crm_monitoring.getUid() 'resource__uid': portal.service_module.slapos_crm_monitoring.getUid()
}
if destination_decision: if destination_decision:
kw['default_destination_decision_uid'] = context.restrictedTraverse( kw['destination_decision__uid'] = context.restrictedTraverse(
destination_decision).getUid() destination_decision).getUid()
support_request_amount = context.portal_catalog.countResults(**kw)[0][0] support_request_amount_list = context.portal_catalog(**kw)
return support_request_amount >= int(limit) return limit <= len(support_request_amount_list)
return CachingMethod(isSupportRequestCreationClosed, 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 @@ ...@@ -50,11 +50,11 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <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>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>InstanceTree_createSupportRequestEvent</string> </value> <value> <string>Entity_createTicketFromTradeCondition</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -4,10 +4,6 @@ from erp5.component.module.DateUtils import addToDate ...@@ -4,10 +4,6 @@ from erp5.component.module.DateUtils import addToDate
instance_tree = context instance_tree = context
portal = context.getPortalObject() portal = context.getPortalObject()
if instance_tree.getMonitorScope() == "disabled":
# Don't generate ticket if Monitor Scope is marked to disable
return
if portal.ERP5Site_isSupportRequestCreationClosed(): if portal.ERP5Site_isSupportRequestCreationClosed():
# Stop ticket creation # Stop ticket creation
return return
...@@ -20,14 +16,11 @@ if (date_check_limit - instance_tree.getCreationDate()) < 0: ...@@ -20,14 +16,11 @@ if (date_check_limit - instance_tree.getCreationDate()) < 0:
software_instance_list = context.portal_catalog( software_instance_list = context.portal_catalog(
portal_type=["Software Instance", "Slave Instance"], portal_type=["Software Instance", "Slave Instance"],
specialise_uid=instance_tree.getUid(), specialise__uid=instance_tree.getUid(),
**{"slapos_item.slap_state": ["start_requested"]}) **{"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 # Check if at least one software Instance is Allocated
notification_message_reference = None
for instance in software_instance_list: for instance in software_instance_list:
if (date_check_limit - instance.getCreationDate()) < 0: if (date_check_limit - instance.getCreationDate()) < 0:
continue continue
...@@ -36,19 +29,48 @@ for instance in software_instance_list: ...@@ -36,19 +29,48 @@ for instance in software_instance_list:
continue continue
compute_partition = instance.getAggregateValue() compute_partition = instance.getAggregateValue()
if compute_partition is not None: if compute_partition is None:
has_newest_allocated_instance = True notification_message_reference = 'slapos-crm-instance-tree-instance-allocation.notification'
if instance.getPortalType() == "Software Instance" and \ elif (instance.getPortalType() == "Software Instance") and \
compute_partition.getParentValue().getMonitorScope() == "enabled" and \ (compute_partition.getParentValue().getMonitorScope() == "enabled") and \
instance.SoftwareInstance_hasReportedError(tolerance=30): 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'
else:
has_unallocated_instance = True if notification_message_reference is not None:
failing_instance = instance ticket_title = "Instance Tree %s is failing." % context.getTitle()
error_message = instance.SoftwareInstance_hasReportedError(include_message=True)
if has_unallocated_instance and has_newest_allocated_instance:
return context.InstanceTree_createSupportRequestEvent( description = "%s contains software instances which are unallocated or reporting errors." % (
failing_instance, 'slapos-crm-instance-tree-instance-allocation.notification') context.getTitle())
if error_message:
return description += "\n\nMessage: %s" % str(error_message)
else:
error_message = "No message!"
project = context.getFollowUpValue()
support_request = project.Project_createSupportRequestWithCausality(
ticket_title,
description,
causality=context.getRelativeUrl(),
destination_decision=context.getDestinationSection()
)
if support_request is None:
return
event = support_request.SupportRequest_getLastEvent(ticket_title)
if event is None:
support_request.Ticket_createProjectEvent(
ticket_title, 'outgoing', 'Web Message',
portal.service_module.slapos_crm_information.getRelativeUrl(),
text_content=description,
content_type='text/plain',
notification_message=notification_message_reference,
#language=XXX,
substitution_method_parameter_dict={
'instance_tree_title':context.getTitle(),
'instance': instance.getTitle(),
'error_text': error_message
}
)
return
portal = context.getPortalObject()
person = context.getDestinationSectionValue()
if person is None or portal.ERP5Site_isSupportRequestCreationClosed(person.getRelativeUrl()):
# Stop ticket creation
return
ticket_title = "Instance Tree %s is failing." % context.getTitle()
error_message = instance.SoftwareInstance_hasReportedError(include_message=True)
description = "%s contains software instances which are unallocated or reporting errors." % (
context.getTitle())
if error_message:
description += "\n\nMessage: %s" % error_message
else:
error_message = "No message!"
support_request = person.Base_getSupportRequestInProgress(
title=ticket_title,
aggregate=context.getRelativeUrl())
if support_request is None:
person.notify(support_request_title=ticket_title,
support_request_description=description,
aggregate=context.getRelativeUrl())
support_request_relative_url = context.REQUEST.get("support_request_relative_url")
if support_request_relative_url is None:
return
support_request = portal.restrictedTraverse(support_request_relative_url)
if support_request is None:
return
if support_request.getSimulationState() not in ["validated", "suspended"]:
support_request.validate()
# Send Notification message
message = description
notification_message = portal.portal_notifications.getDocumentValue(
reference=notification_message_reference)
if notification_message is not None:
mapping_dict = {'instance_tree_title':context.getTitle(),
'instance': instance.getTitle(),
'error_text': error_message}
message = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':mapping_dict})
event = support_request.SupportRequest_getLastEvent(ticket_title)
if event is None:
support_request.notify(message_title=ticket_title, message=message)
return context.REQUEST.get("ticket_notified_item")
from Products.ZSQLCatalog.SQLCatalog import ComplexQuery, SimpleQuery
portal = context.getPortalObject()
person_uid = context.getUid()
query = ComplexQuery(
ComplexQuery(
SimpleQuery(portal_type=["Support Request", "Regularisation Request"]),
SimpleQuery(simulation_state="suspended"),
SimpleQuery(destination_decision_uid=person_uid),
logical_operator='and'),
ComplexQuery(
SimpleQuery(portal_type="Upgrade Decision"),
SimpleQuery(simulation_state="confirmed"),
SimpleQuery(destination_decision_uid=person_uid),
logical_operator='and'),
logical_operator='or')
return portal.portal_catalog(query=query, **kw)
portal = context.getPortalObject()
pending_ticket_list_amount = len(context.Person_getSlapOSPendingTicket())
notification_message = portal.portal_notifications.getDocumentValue(
reference="slapos-crm-person-pending-ticket-notification")
if notification_message is not None:
mapping_dict = {'username': context.getTitle(),
'amount': pending_ticket_list_amount,
'website': portal.portal_preferences.getPreferredSlaposWebSiteUrl()}
return notification_message.getTitle(), notification_message.asText(
substitution_method_parameter_dict={'mapping_dict': mapping_dict})
message = """ You have %s pending tickets """ % pending_ticket_list_amount
return message, message
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_getSlapOSPendingTicketMessageTemplate</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
amount = len(context.Person_getSlapOSPendingTicket())
if amount > 0:
title, reminder_message = context.Person_getSlapOSPendingTicketMessageTemplate()
return context.Person_sendSlapOSPendingTicketNotification(
title,
reminder_message,
batch_mode=1
)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_sendPendingTicketReminder</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
portal = context.getPortalObject()
start_date = DateTime()
# Rely on Trade condition (like in ticket to set the proper sender)
trade_condition = portal.sale_trade_condition_module.slapos_ticket_trade_condition
event_kw = {
'portal_type' : "Mail Message",
'title' : response_event_title,
'resource' : "service_module/slapos_crm_information",
'source' : trade_condition.getSource(),
'destination' : context.getRelativeUrl(),
'start_date' : start_date,
'text_content' : response_event_text_content,
'content_type' : 'text/plain',
}
# Create event
event = portal.event_module.newContent(**event_kw)
event.start(send_mail=True, comment="Sent via Person_sendSlapOSPendingTicketNotification")
if batch_mode:
return event
message = portal.Base_translateString('New event created.')
return event.Base_redirect(keep_items={'portal_status_message': message})
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>response_event_title, response_event_text_content, batch_mode=False, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_sendSlapOSPendingTicketNotification</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>Person_sendSlapOSPendingTicketNotification</string> </value>
</item>
<item>
<key> <string>action_title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list>
<string>listbox</string>
</list>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list>
<string>your_response_event_title</string>
<string>your_response_event_text_content</string>
</list>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list>
<string>listbox_delivery_start_date</string>
</list>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>my_title</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_viewSlapOSPendingTicketDialog</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>Person_viewSlapOSPendingTicket</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_dialog</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Send Pending Ticket Report</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>columns</string>
<string>default_params</string>
<string>description</string>
<string>editable</string>
<string>editable_columns</string>
<string>list_method</string>
<string>portal_types</string>
<string>selection_name</string>
<string>sort</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>listbox</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>columns</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable_columns</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>list_method</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>portal_types</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>selection_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>sort</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>columns</string> </key>
<value>
<list>
<tuple>
<string>getTitle</string>
<string>Title</string>
</tuple>
<tuple>
<string>translated_portal_type</string>
<string>Event Type</string>
</tuple>
<tuple>
<string>delivery.start_date</string>
<string>Date</string>
</tuple>
<tuple>
<string>translated_simulation_state_title</string>
<string>State</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>default_params</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>List of all Support Requests related to the follow up ticket</string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>editable_columns</string> </key>
<value>
<list>
<tuple>
<string>delivery.start_date</string>
<string>Date</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_listbox</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>list_method</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>portal_types</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>selection_name</string> </key>
<value> <string>person_pending_ticket_view_selection</string> </value>
</item>
<item>
<key> <string>sort</string> </key>
<value>
<list>
<tuple>
<string>delivery.start_date</string>
<string>asc</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Tickets</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Method" module="Products.Formulator.MethodField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>method_name</string> </key>
<value> <string>Person_getSlapOSPendingTicket</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="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>
...@@ -50,11 +50,11 @@ ...@@ -50,11 +50,11 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>**kw</string> </value> <value> <string>title, text_content, causality, destination_decision</string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_getSlapOSPendingTicket</string> </value> <value> <string>Project_createSupportRequestWithCausality</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
This diff is collapsed.
This diff is collapsed.
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