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

slapos_crm: support for the virtual master logic

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

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

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

Prevent allocation if a customer has an ongoing Regularisation Request.

TODO: handle case of unpaid invoices for professional customers

Also in this commit:
* drop templates
* use interaction workflow to trigger alarms
parent 286a3bcc
......@@ -42,7 +42,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>jump_to_related_upgrade_decision_line</string> </value>
<value> <string>jump_to_related_upgrade_decision</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
......@@ -62,7 +62,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Related Upgrade Decision Line</string> </value>
<value> <string>Related Upgrade Decision</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
......@@ -81,7 +81,7 @@
<key> <string>text</string> </key>
<value> <string encoding="cdata"><![CDATA[
string:${object_url}/Base_jumpToRelatedObjectList?base_category=aggregate&portal_type=Upgrade%20Decision%20%Line&simulation_state=confirmed
string:${object_url}/Base_jumpToRelatedObjectList?base_category=aggregate&portal_type=Upgrade%20Decision&simulation_state=confirmed
]]></string> </value>
</item>
......@@ -96,7 +96,7 @@ string:${object_url}/Base_jumpToRelatedObjectList?base_category=aggregate&portal
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: portal.Base_checkPermission(\'subscription_request_module\', \'View\')</string> </value>
<value> <string>python: portal.Base_checkPermission(\'upgrade_decision_module\', \'View\')</string> </value>
</item>
</dictionary>
</pickle>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_user_pending_ticket_report</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>50.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Send Pending Ticket Report</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/Person_viewSlapOSPendingTicketDialog</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>update_destination_for_slapos</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>5.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Update Destination with all Slapos Users</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/SiteMessage_setSlapOSUserSourceAndDestinatationList</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: object.getSimulationState() in (\'draft\',)</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_jump</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_jump</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>jump_related_slapos_item</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>14.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Related Instance Tree, Compute Node or Software Installation</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string encoding="cdata"><![CDATA[
string:${object_url}/Base_jumpToRelationObject?base_category=aggregate&portal_type:list=Instance%20Tree&portal_type:list=Compute%20Node&portal_type:list=Software%20Installation
]]></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<module>
<category_list>
<category>business_application/crm</category>
</category_list>
<id>incident_response_module</id>
<permission_list>
<permission type='tuple'>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Person" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>__translation_dict</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>allocation_tester</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>first_name</string> </key>
<value> <string>Member</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>allocation_tester</string> </value>
</item>
<item>
<key> <string>last_name</string> </key>
<value> <string>Template</string> </value>
</item>
<item>
<key> <string>password</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Person</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Allocation tester</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value> <string>{SSHA}f1gAG3A53rfwjkLB/+Ex89MtocZz/4V9K4TZ</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -10,13 +10,17 @@
<key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_checkComputeNodeState</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Check if a public or a friend compute_node contacted master recently and create a ticket if the compute_node stops to contact master after some time.</string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>0</int> </value>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -10,13 +10,17 @@
<key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_checkInstanceTreeState</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Check and create a Ticket when an instance is partially allocated for more than 4 hours.</string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>0</int> </value>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -22,7 +22,7 @@
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>0</int> </value>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Alarm" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>active_sense_method_id</string> </key>
<value> <string>Alarm_sendPendingTicketReminder</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Send a Mail Message with a Reminder in case the user has Tickets to respond</string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_crm_send_pending_ticket_reminder</string> </value>
</item>
<item>
<key> <string>periodicity_hour</string> </key>
<value>
<tuple>
<int>6</int>
</tuple>
</value>
</item>
<item>
<key> <string>periodicity_hour_frequency</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>periodicity_minute</string> </key>
<value>
<tuple>
<int>0</int>
</tuple>
</value>
</item>
<item>
<key> <string>periodicity_minute_frequency</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>periodicity_month</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_month_day</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_start_date</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1288051200.0</float>
<string>GMT</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>periodicity_week</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_week_day</string> </key>
<value>
<tuple>
<string>Tuesday</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Alarm</string> </value>
</item>
<item>
<key> <string>sense_method_id</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Check compute_node\'s state</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="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>
<portal_type id="Compute Node">
<item>monitor_scope</item>
<item>upgrade_scope</item>
</portal_type>
<portal_type id="Incident Response">
<item>aggregate</item>
......@@ -12,7 +11,6 @@
</portal_type>
<portal_type id="Instance Tree">
<item>monitor_scope</item>
<item>upgrade_scope</item>
</portal_type>
<portal_type id="Regularisation Request">
<item>specialise</item>
......
......@@ -8,9 +8,6 @@
<portal_type id="Site Message">
<item>SlapOSEventConstraint</item>
</portal_type>
<portal_type id="Support Request">
<item>SlapOSSupportRequestConstraint</item>
</portal_type>
<portal_type id="Web Message">
<item>SlapOSEventConstraint</item>
</portal_type>
......
......@@ -12,10 +12,20 @@
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>acquire_local_roles</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>content_icon</string> </key>
<value> <string>folder_icon.gif</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>factory</string> </key>
<value> <string>addFolder</string> </value>
......@@ -32,6 +42,18 @@
<key> <string>id</string> </key>
<value> <string>Regularisation Request Module</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Base Type</string> </value>
......@@ -40,6 +62,12 @@
<key> <string>type_class</string> </key>
<value> <string>Folder</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
......
<workflow_chain>
<chain>
<type>Compute Node</type>
<workflow>slapos_crm_interaction_workflow</workflow>
</chain>
<chain>
<type>Incident Response</type>
<workflow>edit_workflow, ticket_workflow</workflow>
</chain>
<chain>
<type>Instance Tree</type>
<workflow>slapos_crm_interaction_workflow</workflow>
</chain>
<chain>
<type>Regularisation Request</type>
<workflow>edit_workflow, pricing_interaction_workflow, ticket_interaction_workflow, ticket_slap_interface_workflow, ticket_workflow</workflow>
<workflow>edit_workflow, pricing_interaction_workflow, slapos_crm_interaction_workflow, ticket_interaction_workflow, ticket_workflow</workflow>
</chain>
<chain>
<type>Sale Invoice Transaction</type>
<workflow>slapos_crm_interaction_workflow</workflow>
</chain>
<chain>
<type>Support Request</type>
<workflow>ticket_slap_interface_workflow</workflow>
<type>Sale Invoice Transaction Line</type>
<workflow>slapos_crm_interaction_workflow</workflow>
</chain>
</workflow_chain>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/boolean</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_cloud_contract_enabled_property</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: False</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/string</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_regularisation_request_template_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: \'\'</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/string</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_support_request_template_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: \'\'</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/string</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_web_message_template_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: \'\'</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Property Sheet" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SlapOSSupportRequestConstraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Property Sheet</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Script Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>constraint_type/default</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>causality_source_destination_constraint_constraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Script Constraint</string> </value>
</item>
<item>
<key> <string>script_id</string> </key>
<value> <string>SupportRequest_checkCausalitySourceDestinationConsistency</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -3,28 +3,23 @@ portal = context.getPortalObject()
person_uid_list = []
for (_, brain) in enumerate(portal.portal_simulation.getInventoryList(
simulation_state=('stopped', 'delivered'),
parent_payment_mode_uid = [
portal.portal_categories.payment_mode.payzen.getUid(),
portal.portal_categories.payment_mode.wechat.getUid()],
group_by_mirror_section=True,
portal_type=portal.getPortalAccountingMovementTypeList(),
node_uid=[x.uid for x in context.Base_getReceivableAccountList()],
grouping_reference=None)):
node_uid=[x.uid for x in context.Base_getReceivableAccountList()] or -1,
parent__ledger__uid=portal.portal_categories.ledger.automated.getUid(),
grouping_reference=None
)):
payment_request_uid = brain.payment_request_uid
if not payment_request_uid:
payment_request_uid = brain.getObject().getExplanationUid()
payment_request = portal.portal_catalog.getObject(uid=payment_request_uid)
section_uid = payment_request.getDestinationSectionUid(portal_type="Person")
section_uid = brain.getDestinationSectionUid(portal_type="Person")
if section_uid is not None:
person_uid_list.append(section_uid)
portal.portal_catalog.searchAndActivate(
portal_type="Person",
validation_state="validated",
uid=person_uid_list,
method_id='Person_checkToCreateRegularisationRequest',
activate_kw={'tag': tag}
)
if person_uid_list:
portal.portal_catalog.searchAndActivate(
portal_type="Person",
validation_state="validated",
uid=person_uid_list,
method_id='Person_checkToCreateRegularisationRequest',
activate_kw={'tag': tag}
)
context.activate(after_tag=tag).getId()
portal = context.getPortalObject()
sub_tag = "RegularisationRequest_deleteInstanceTreeList"
portal.portal_catalog.searchAndActivate(
portal_type="Regularisation Request",
portal_type="Regularisation Request",
simulation_state=["suspended"],
default_resource_uid=portal.service_module.slapos_crm_delete_acknowledgement.getUid(),
resource__uid=portal.service_module.slapos_crm_delete_acknowledgement.getUid(),
method_id='RegularisationRequest_deleteInstanceTreeList',
method_args=(sub_tag,),
# Limit activity number, as method_id also calls searchAndActivate
......
portal = context.getPortalObject()
sub_tag = "RegularisationRequest_stopInstanceTreeList"
portal.portal_catalog.searchAndActivate(
portal_type="Regularisation Request",
portal_type="Regularisation Request",
simulation_state=["suspended"],
default_resource_uid=[
resource__uid=[
portal.service_module.slapos_crm_stop_acknowledgement.getUid(),
portal.service_module.slapos_crm_delete_reminder.getUid(),
portal.service_module.slapos_crm_delete_acknowledgement.getUid(),
......
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Regularisation Request",
portal_type="Regularisation Request",
simulation_state=["suspended"],
default_resource_uid=portal.service_module.slapos_crm_acknowledgement.getUid(),
resource__uid=portal.service_module.slapos_crm_acknowledgement.getUid(),
method_id='RegularisationRequest_triggerAcknowledgmentEscalation',
activate_kw={'tag': tag}
)
......
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Regularisation Request",
portal_type="Regularisation Request",
simulation_state=["suspended"],
default_resource_uid=portal.service_module.slapos_crm_stop_reminder.getUid(),
resource__uid=portal.service_module.slapos_crm_stop_reminder.getUid(),
method_id='RegularisationRequest_triggerStopReminderEscalation',
activate_kw={'tag': tag}
)
......
......@@ -15,7 +15,8 @@ if (slap_state in ['start_requested', 'stop_requested']):
software_type=instance_tree.getSourceReference(),
instance_xml=instance_tree.getTextContent(),
sla_xml=instance_tree.getSlaXml(),
shared=instance_tree.isRootSlave()
shared=instance_tree.isRootSlave(),
project_reference=instance_tree.getFollowUpReference()
)
return True
return False
......@@ -15,7 +15,8 @@ if (slap_state == 'start_requested'):
software_type=instance_tree.getSourceReference(),
instance_xml=instance_tree.getTextContent(),
sla_xml=instance_tree.getSlaXml(),
shared=instance_tree.isRootSlave()
shared=instance_tree.isRootSlave(),
project_reference=instance_tree.getFollowUpReference()
)
return True
return False
......@@ -16,27 +16,15 @@ ticket_portal_type = "Regularisation Request"
ticket = portal.portal_catalog.getResultValue(
portal_type=ticket_portal_type,
default_source_project_uid=person.getUid(),
destination__uid=person.getUid(),
simulation_state=['suspended', 'validated'],
)
if ticket is not None:
return ticket, None
outstanding_amount = person.Entity_statSlapOSOutstandingAmount()
# Amount to be ignored, as it comes from the first invoice generated
# after the subscription. We do not take it into account as no service
# was provided yet.
unpaid_invoice_amount = 0
for invoice in person.Person_getSubscriptionRequestFirstUnpaidInvoiceList():
unpaid_invoice_amount += invoice.getTotalPrice()
# It can't be smaller, we are considernig all open invoices are from unpaid_payment_amount
if round(float(outstanding_amount), 2) == round(float(unpaid_invoice_amount), 2):
return ticket, None
if int(outstanding_amount) > 0:
mail_message = None
if person.Entity_hasOutstandingAmount(ledger_uid=portal.portal_categories.ledger.automated.getUid()):
tag = "%s_addRegularisationRequest_inProgress" % person.getUid()
if (portal.portal_activities.countMessageWithTag(tag) > 0):
# The regularisation request is already under creation but can not be fetched from catalog
......@@ -47,59 +35,39 @@ if int(outstanding_amount) > 0:
person.serialize()
# Time to create the ticket
regularisation_request_template = portal.restrictedTraverse(
portal.portal_preferences.getPreferredRegularisationRequestTemplate())
ticket = regularisation_request_template.Base_createCloneDocument(batch_mode=1)
ticket.edit(
source_project_value=context,
title='Account regularisation expected for "%s"' % context.getTitle(),
destination_decision_value=context,
destination_value=context,
start_date=DateTime(),
resource=portal.portal_preferences.getPreferredRegularisationRequestResource(),
comment = 'New automatic ticket for %s' % context.getTitle()
ticket = context.Entity_createTicketFromTradeCondition(
portal.service_module.slapos_crm_monitoring.getRelativeUrl(),
'Account regularisation expected for "%s"' % context.getTitle(),
'',
portal_type='Regularisation Request',
comment=comment
)
ticket.validate(comment='New automatic ticket for %s' % context.getTitle())
ticket.suspend(comment='New automatic ticket for %s' % context.getTitle())
ticket.validate(comment=comment)
ticket.suspend(comment=comment)
ticket.reindexObject(activate_kw={'tag': tag})
# Notify using user's language
language = context.getLanguage("en")
notification_message = context.getPortalObject().portal_notifications.getDocumentValue(
reference="slapos-crm.create.regularisation.request",
language=language)
if notification_message is None:
subject = 'Invoice payment requested'
body = """Dear %s,
subject = 'Invoice payment requested'
body = """Dear %s,
A new invoice has been generated.
A new invoice has been generated.
You can access it in your invoice section at %s.
Regards,
The slapos team
""" % (context.getTitle(), portal.portal_preferences.getPreferredSlaposWebSiteUrl())
else:
notification_mapping_dict = {
'user_name': context.getTitle()}
subject = notification_message.getTitle()
# Preserve HTML else convert to text
if notification_message.getContentType() == "text/html":
body = notification_message.asEntireHTML(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
else:
body = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
notification_message_reference = "slapos-crm.create.regularisation.request"
mail_message = ticket.RegularisationRequest_checkToSendUniqEvent(
portal.portal_preferences.getPreferredRegularisationRequestResource(),
subject,
body,
'Requested manual payment.')
return ticket, mail_message
'Requested manual payment.',
notification_message=notification_message_reference,
substitution_method_parameter_dict={
'user_name': context.getTitle()
},
)
return ticket, None
return ticket, mail_message
portal = context.getPortalObject()
from erp5.component.module.DateUtils import addToDate
from Products.ZSQLCatalog.SQLCatalog import Query
from DateTime import DateTime
unpaid_list = []
subscription_request_list = portal.portal_catalog(
portal_type="Subscription Request",
simulation_state=["ordered", "confirmed"],
default_destination_section_uid=context.getUid(),
# Select "Subscription Request" with most likely unpaid invoices, recently generated.
creation_date=Query(creation_date=addToDate(DateTime(), to_add={'day': -20}), range="min"))
for subscription_request in subscription_request_list:
first_invoice = subscription_request.SubscriptionRequest_verifyPaymentBalanceIsReady()
if first_invoice is not None and not first_invoice.SaleInvoiceTransaction_isLettered():
unpaid_list.append(first_invoice)
return unpaid_list
......@@ -18,8 +18,8 @@ event_portal_type = "Mail Message"
event = portal.portal_catalog.getResultValue(
portal_type=event_portal_type,
default_resource_uid=service.getUid(),
default_follow_up_uid=ticket.getUid(),
resource__uid=service.getUid(),
follow_up__uid=ticket.getUid(),
)
if (event is None) and (ticket.getSimulationState() == 'suspended'):
......@@ -31,20 +31,15 @@ if (event is None) and (ticket.getSimulationState() == 'suspended'):
# Prevent concurrent transaction to create 2 events for the same ticket
ticket.edit(resource=service_relative_url)
event = portal.event_module.newContent(
portal_type=event_portal_type,
start_date=DateTime(),
destination=ticket.getDestination(),
follow_up=ticket.getRelativeUrl(),
source=context.getSource(),
title=title,
resource=service_relative_url,
event = ticket.Ticket_createProjectEvent(
title, 'outgoing', 'Mail Message',
service_relative_url,
text_content=text_content,
content_type='text/plain',
notification_message=notification_message,
substitution_method_parameter_dict=substitution_method_parameter_dict,
comment=comment
)
event.start(send_mail=True, comment=comment)
event.stop(comment=comment)
event.deliver(comment=comment)
event.reindexObject(activate_kw={'tag': tag})
return event
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>service_relative_url, title, text_content, comment, REQUEST=None</string> </value>
<value> <string>service_relative_url, title, text_content, comment, notification_message=None, substitution_method_parameter_dict=None, REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -11,14 +11,16 @@ event_portal_type = "Mail Message"
event = portal.portal_catalog.getResultValue(
portal_type=event_portal_type,
default_resource_uid=current_service.getUid(),
default_follow_up_uid=ticket.getUid(),
resource__uid=current_service.getUid(),
follow_up__uid=ticket.getUid(),
simulation_state="delivered",
)
if (ticket.getSimulationState() == 'suspended') and (event is not None) and (ticket.getResource() == current_service_relative_url):
if (DateTime() - event.getStartDate()) > delay_period_in_days:
ticket.RegularisationRequest_checkToSendUniqEvent(next_service_relative_url, title, text_content, comment)
ticket.RegularisationRequest_checkToSendUniqEvent(next_service_relative_url, title, text_content, comment,
notification_message=notification_message,
substitution_method_parameter_dict=substitution_method_parameter_dict)
return event.getRelativeUrl()
return None
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment, REQUEST=None</string> </value>
<value> <string>delay_period_in_days, current_service_relative_url, next_service_relative_url, title, text_content, comment, notification_message=None, substitution_method_parameter_dict=None, REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -4,19 +4,66 @@ if REQUEST is not None:
ticket = context
state = ticket.getSimulationState()
person = ticket.getSourceProjectValue(portal_type="Person")
person = ticket.getDestinationDecisionValue(portal_type="Person")
if (state == 'suspended') and \
(person is not None) and \
(ticket.getResource() == 'service_module/slapos_crm_delete_acknowledgement'):
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Instance Tree",
validation_state=["validated"],
default_destination_section_uid=person.getUid(),
method_id='InstanceTree_deleteFromRegularisationRequest',
method_args=(person.getRelativeUrl(),),
activate_kw={'tag': tag}
)
subscribed_item_list = []
ledger_uid = portal.portal_categories.ledger.automated.getUid()
# Gather the list of not paid services
for outstanding_amount in person.Entity_getOutstandingAmountList(
ledger_uid=ledger_uid,
include_planned=True
):
for outstanding_invoice in person.Entity_getOutstandingAmountList(
section_uid=outstanding_amount.getSourceSectionUid(),
resource_uid=outstanding_amount.getPriceCurrencyUid(),
ledger_uid=outstanding_amount.getLedgerUid(),
group_by_node=False
):
subscribed_item = None
for invoice_line in outstanding_invoice.getMovementList(
portal_type=['Invoice Line', 'Invoice Cell']
):
hosting_subscription = invoice_line.getAggregateValue(portal_type='Hosting Subscription')
if hosting_subscription is not None:
subscribed_item = invoice_line.getAggregateValue(portal_type=[
'Project',
'Instance Tree',
'Compute Node'
])
if subscribed_item is None:
raise NotImplementedError('Unhandled invoice line %s' % invoice_line.getRelativeUrl())
subscribed_item_list.append(subscribed_item)
if subscribed_item is None:
raise NotImplementedError('Unhandled invoice %s' % outstanding_invoice.getRelativeUrl())
for subscribed_item in subscribed_item_list:
if ((subscribed_item.getPortalType() == 'Compute Node') and
(subscribed_item.getAllocationScope() != 'close/forever')):
# allow cleaning up the compute node even if deleted
subscribed_item.edit(allocation_scope='close/forever')
elif (subscribed_item.getPortalType() == 'Instance Tree'):
# change the slap state to deleted, to allow propagation of the state
# even on remote node
subscribed_item.InstanceTree_deleteFromRegularisationRequest(person.getRelativeUrl())
elif ((subscribed_item.getPortalType() == 'Project') and
(subscribed_item.getValidationState() != 'invalidated')):
# do not close the project until all node and instance trees are corrected deleted
can_invalidate_project = True
for other_item in portal.portal_catalog(
portal_type=['Compute Node', 'Instance Tree'],
follow_up__uid=subscribed_item.getUid()
):
if other_item.getValidationState() not in ['invalidated', 'archived']:
can_invalidate_project = False
subscribed_item_list.append(other_item)
if can_invalidate_project:
subscribed_item.invalidate(comment='Not paid')
return True
return False
......@@ -3,26 +3,10 @@ if REQUEST is not None:
raise Unauthorized
state = context.getSimulationState()
person = context.getSourceProjectValue(portal_type="Person")
person = context.getDestinationDecisionValue(portal_type="Person")
if (state not in ('suspended', 'validated')) or \
(person is None):
return
outstanding_amount = person.Entity_statSlapOSOutstandingAmount()
# Amount to be ignored, as it comes from the first invoice generated
# after the subscription. We do not take it into account as no service
# was provided yet.
unpaid_invoice_amount = 0
for invoice in person.Person_getSubscriptionRequestFirstUnpaidInvoiceList():
unpaid_invoice_amount += invoice.getTotalPrice()
# It can't be smaller, we are considernig all open invoices are from unpaid_payment_amount
if round(float(outstanding_amount), 2) == round(float(unpaid_invoice_amount), 2):
context.invalidate(comment="Automatically disabled as balance is %s" % outstanding_amount)
return
if (int(outstanding_amount) > 0):
return
context.invalidate(comment="Automatically disabled as balance is %s" % outstanding_amount)
if not person.Entity_hasOutstandingAmount(ledger_uid=context.getPortalObject().portal_categories.ledger.automated.getUid()):
context.invalidate(comment="Automatically disabled as balance is ok")
......@@ -4,16 +4,16 @@ if REQUEST is not None:
ticket = context
state = ticket.getSimulationState()
person = ticket.getSourceProjectValue(portal_type="Person")
person = ticket.getDestinationDecisionValue(portal_type="Person")
if (state == 'suspended') and \
(person is not None) and \
(ticket.getResource() in ['service_module/slapos_crm_stop_acknowledgement', 'service_module/slapos_crm_delete_reminder', 'service_module/slapos_crm_delete_acknowledgement']):
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Instance Tree",
validation_state=["validated"],
default_destination_section_uid=person.getUid(),
destination_section__uid=person.getUid(),
method_id='InstanceTree_stopFromRegularisationRequest',
method_args=(person.getRelativeUrl(),),
activate_kw={'tag': tag}
......
......@@ -2,19 +2,10 @@ from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
portal = context.getPortalObject()
ndays = 15
language = "en"
recipient = context.getDestinationSectionValue()
if recipient is not None:
language = recipient.getLanguage("en")
notification_message = portal.portal_notifications.getDocumentValue(
language=language, reference="slapos-crm.acknowledgment.escalation")
if notification_message is None:
subject = 'Reminder: invoice payment requested'
body = """Dear user,
subject = 'Reminder: invoice payment requested'
body = """Dear user,
We would like to remind you the unpaid invoice you have on %s.
If no payment is done during the coming days, we will stop all your current instances to free some hardware resources.
......@@ -23,26 +14,16 @@ Regards,
The slapos team
""" % context.getPortalObject().portal_preferences.getPreferredSlaposWebSiteUrl()
else:
notification_mapping_dict = {
'user_name': context.getDestinationSectionTitle(),
'days': ndays}
subject = notification_message.getTitle()
# Preserve HTML else convert to text
if notification_message.getContentType() == "text/html":
body = notification_message.asEntireHTML(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
else:
body = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
return context.RegularisationRequest_checkToTriggerNextEscalationStep(
delay_period_in_days=ndays,
current_service_relative_url='service_module/slapos_crm_acknowledgement',
next_service_relative_url='service_module/slapos_crm_stop_reminder',
next_service_relative_url='service_module/slapos_crm_delete_reminder',
title=subject,
text_content=body,
comment='Stopping reminder.',
notification_message="slapos-crm.acknowledgment.escalation",
substitution_method_parameter_dict={
'user_name': context.getDestinationSectionTitle(),
'days': ndays
}
)
......@@ -2,19 +2,10 @@ from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
portal = context.getPortalObject()
ndays = 10
language = "en"
recipient = context.getDestinationSectionValue()
if recipient is not None:
language = recipient.getLanguage("en")
notification_message = portal.portal_notifications.getDocumentValue(
language=language, reference="slapos-crm.delete.reminder.escalation")
if notification_message is None:
subject = 'Acknowledgment: instances deleted'
body = """Dear user,
subject = 'Acknowledgment: instances deleted'
body = """Dear user,
Despite our last reminder, you still have an unpaid invoice on %s.
We will now delete all your instances.
......@@ -22,20 +13,6 @@ We will now delete all your instances.
Regards,
The slapos team
""" % context.getPortalObject().portal_preferences.getPreferredSlaposWebSiteUrl()
else:
notification_mapping_dict = {
'user_name': context.getDestinationSectionTitle(),
'days': ndays}
subject = notification_message.getTitle()
# Preserve HTML else convert to text
if notification_message.getContentType() == "text/html":
body = notification_message.asEntireHTML(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
else:
body = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
return context.RegularisationRequest_checkToTriggerNextEscalationStep(
delay_period_in_days=ndays,
......@@ -44,4 +21,9 @@ return context.RegularisationRequest_checkToTriggerNextEscalationStep(
title=subject,
text_content=body,
comment='Deleting acknowledgment.',
notification_message="slapos-crm.delete.reminder.escalation",
substitution_method_parameter_dict={
'user_name': context.getDestinationSectionTitle(),
'days': ndays
}
)
......@@ -2,19 +2,9 @@ from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
portal = context.getPortalObject()
ndays = 7
language = "en"
recipient = context.getDestinationSectionValue()
if recipient is not None:
language = recipient.getLanguage("en")
notification_message = portal.portal_notifications.getDocumentValue(
language=language, reference="slapos-crm.stop.acknowledgment.escalation")
if notification_message is None:
subject = 'Last reminder: invoice payment requested'
body = """Dear user,
subject = 'Last reminder: invoice payment requested'
body = """Dear user,
We would like to remind you the unpaid invoice you have on %s.
If no payment is done during the coming days, we will delete all your instances.
......@@ -22,20 +12,6 @@ If no payment is done during the coming days, we will delete all your instances.
Regards,
The slapos team
""" % context.getPortalObject().portal_preferences.getPreferredSlaposWebSiteUrl()
else:
notification_mapping_dict = {
'user_name': context.getDestinationSectionTitle(),
'days': ndays}
subject = notification_message.getTitle()
# Preserve HTML else convert to text
if notification_message.getContentType() == "text/html":
body = notification_message.asEntireHTML(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
else:
body = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
return context.RegularisationRequest_checkToTriggerNextEscalationStep(
delay_period_in_days=ndays,
......@@ -44,4 +20,9 @@ return context.RegularisationRequest_checkToTriggerNextEscalationStep(
title=subject,
text_content=body,
comment='Deleting reminder.',
notification_message="slapos-crm.stop.acknowledgment.escalation",
substitution_method_parameter_dict={
'user_name': context.getDestinationSectionTitle(),
'days': ndays
}
)
......@@ -2,19 +2,10 @@ from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
portal = context.getPortalObject()
ndays = 7
language = "en"
recipient = context.getDestinationSectionValue()
if recipient is not None:
language = recipient.getLanguage("en")
notification_message = portal.portal_notifications.getDocumentValue(
language=language, reference="slapos-crm.stop.reminder.escalation")
if notification_message is None:
subject = 'Acknowledgment: instances stopped'
body = """Dear user,
subject = 'Acknowledgment: instances stopped'
body = """Dear user,
Despite our last reminder, you still have an unpaid invoice on %s.
We will now stop all your current instances to free some hardware resources.
......@@ -22,20 +13,6 @@ We will now stop all your current instances to free some hardware resources.
Regards,
The slapos team
""" % context.getPortalObject().portal_preferences.getPreferredSlaposWebSiteUrl()
else:
notification_mapping_dict = {
'user_name': context.getDestinationSectionTitle(),
'days': ndays}
subject = notification_message.getTitle()
# Preserve HTML else convert to text
if notification_message.getContentType() == "text/html":
body = notification_message.asEntireHTML(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
else:
body = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':notification_mapping_dict})
return context.RegularisationRequest_checkToTriggerNextEscalationStep(
delay_period_in_days=ndays,
......@@ -44,4 +21,9 @@ return context.RegularisationRequest_checkToTriggerNextEscalationStep(
title=subject,
text_content=body,
comment='Stopping acknowledgment.',
notification_message="slapos-crm.stop.reminder.escalation",
substitution_method_parameter_dict={
'user_name': context.getDestinationSectionTitle(),
'days': ndays
}
)
......@@ -9,11 +9,11 @@ monitor_enabled_category = portal.restrictedTraverse(
if monitor_enabled_category is not None:
portal.portal_catalog.searchAndActivate(
portal_type = 'Compute Node',
validation_state = 'validated',
default_monitor_scope_uid = monitor_enabled_category.getUid(),
method_id = 'ComputeNode_checkState',
activate_kw = {'tag':tag}
portal_type='Compute Node',
validation_state='validated',
monitor_scope__uid=monitor_enabled_category.getUid(),
method_id='ComputeNode_checkState',
activate_kw={'tag':tag}
)
context.activate(after_tag=tag).getId()
......@@ -9,11 +9,11 @@ monitor_enabled_category = portal.restrictedTraverse(
if monitor_enabled_category is not None:
portal.portal_catalog.searchAndActivate(
portal_type = 'Compute Node',
validation_state = 'validated',
default_monitor_scope_uid = monitor_enabled_category.getUid(),
method_id = 'ComputeNode_checkSoftwareInstallationState',
activate_kw = {'tag':tag}
portal_type='Compute Node',
validation_state='validated',
monitor_scope__uid=monitor_enabled_category.getUid(),
method_id='ComputeNode_checkSoftwareInstallationState',
activate_kw={'tag':tag}
)
context.activate(after_tag=tag).getId()
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type = 'Person',
method_id = 'Person_sendPendingTicketReminder',
activate_kw = {'tag':tag}
)
context.activate(after_tag=tag).getId()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>tag, fixit, params</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Alarm_sendPendingTicketReminder</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -3,11 +3,11 @@ portal = context.getPortalObject()
default_resource_uid = portal.restrictedTraverse("service_module/slapos_crm_monitoring", None).getUid()
portal.portal_catalog.searchAndActivate(
portal_type='Support Request',
simulation_state=['validated', 'suspended'],
default_resource_uid=default_resource_uid,
default_aggregate_portal_type=["Instance Tree"],
simulation_state=['submitted', 'validated', 'suspended'],
resource__uid=default_resource_uid,
aggregate__portal_type=["Instance Tree"],
method_id='SupportRequest_updateMonitoringState',
activate_kw = {'tag':tag}
activate_kw={'tag':tag}
)
context.activate(after_tag=tag).getId()
from DateTime import DateTime
portal = context.getPortalObject()
person = context.getSourceAdministrationValue(portal_type="Person")
if not person or \
context.getMonitorScope() == "disabled" or \
if (context.getMonitorScope() == "disabled") or \
portal.ERP5Site_isSupportRequestCreationClosed():
return
return
software_installation_list = portal.portal_catalog(
portal_type='Software Installation',
default_aggregate_uid=context.getUid(),
aggregate__uid=context.getUid(),
validation_state='validated',
sort_on=(('creation_date', 'DESC'),)
)
......@@ -26,15 +24,20 @@ for software_installation in software_installation_list:
# Give it 12 hours to deploy.
continue
if software_installation.getSlapState() != 'start_requested':
continue
reference = software_installation.getReference()
d = software_installation.getAccessStatus()
if d.get("no_data", None) == 1:
should_notify = True
last_contact = "No Contact Information"
ticket_title = "[MONITORING] No information for %s on %s" % (reference, compute_node_reference)
description = "The software release %s did not started to build on %s since %s" % \
(software_installation.getUrlString(), compute_node_title, software_installation.getCreationDate())
else:
last_contact = DateTime(d.get('created_at'))
if d.get("text").startswith("building"):
if d.get("text").startswith("#building"):
should_notify = True
ticket_title = "[MONITORING] %s is building for too long on %s" % (reference, compute_node_reference)
description = "The software release %s is building for mode them 12 hours on %s, started on %s" % \
......@@ -49,42 +52,34 @@ for software_installation in software_installation_list:
(software_installation.getUrlString(), compute_node_title, software_installation.getCreationDate())
if should_notify:
support_request = person.Base_getSupportRequestInProgress(
title=ticket_title,
aggregate=software_installation.getRelativeUrl())
if support_request is None:
person.notify(support_request_title=ticket_title,
support_request_description=description,
aggregate=software_installation.getRelativeUrl())
support_request_relative_url = context.REQUEST.get("support_request_relative_url")
if support_request_relative_url is None:
return
project = context.getFollowUpValue()
support_request = project.Project_createSupportRequestWithCausality(
ticket_title,
description,
causality=context.getRelativeUrl(),
destination_decision=project.getDestination()
)
support_request = portal.restrictedTraverse(support_request_relative_url)
if support_request is None:
return
# Send Notification message
notification_reference = 'slapos-crm-compute_node_software_installation_state.notification'
notification_message = portal.portal_notifications.getDocumentValue(
reference=notification_reference)
if notification_message is None:
message = """%s""" % description
else:
mapping_dict = {'compute_node_title':context.getTitle(),
'compute_node_id':reference,
'last_contact':last_contact}
message = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':mapping_dict})
notification_message_reference = 'slapos-crm-compute_node_software_installation_state.notification'
event = support_request.SupportRequest_getLastEvent(ticket_title)
if event is None:
support_request.notify(message_title=ticket_title, message=message)
support_request.Ticket_createProjectEvent(
ticket_title, 'outgoing', 'Web Message',
portal.service_module.slapos_crm_information.getRelativeUrl(),
text_content=description,
content_type='text/plain',
notification_message=notification_message_reference,
#language=XXX,
substitution_method_parameter_dict={
'compute_node_title':context.getTitle(),
'compute_node_id':reference,
'last_contact':last_contact
}
)
support_request_list.append(support_request)
......
from DateTime import DateTime
portal = context.getPortalObject()
person = context.getSourceAdministrationValue(portal_type="Person")
if not person or \
context.getMonitorScope() == "disabled" or \
if (context.getMonitorScope() == "disabled") or \
portal.ERP5Site_isSupportRequestCreationClosed():
return
......@@ -56,7 +54,7 @@ if not should_notify:
if compute_partition_uid_list:
instance_list = portal.portal_catalog(
portal_type='Software Instance',
default_aggregate_uid=compute_partition_uid_list)
aggregate__uid=compute_partition_uid_list)
if instance_list:
should_notify = True
......@@ -78,45 +76,36 @@ if not should_notify:
context.getTitle(), context.getReference(), last_contact)
if should_notify:
support_request = person.Base_getSupportRequestInProgress(
title=node_ticket_title,
aggregate=context.getRelativeUrl())
support_request = context.Base_getSupportRequestInProgress(
title=node_ticket_title)
if support_request is None:
support_request = person.Base_getSupportRequestInProgress(
title=ticket_title,
aggregate=context.getRelativeUrl())
if support_request is None:
person.notify(support_request_title=ticket_title,
support_request_description=description,
aggregate=context.getRelativeUrl())
support_request_relative_url = context.REQUEST.get("support_request_relative_url")
if support_request_relative_url is None:
return
support_request = portal.restrictedTraverse(support_request_relative_url)
project = context.getFollowUpValue()
support_request = project.Project_createSupportRequestWithCausality(
ticket_title,
description,
causality=context.getRelativeUrl(),
destination_decision=project.getDestination()
)
if support_request is None:
return
# Send Notification message
notification_message = portal.portal_notifications.getDocumentValue(
reference=notification_message_reference)
if notification_message is None:
message = """%s""" % description
else:
mapping_dict = {'compute_node_title':context.getTitle(),
'compute_node_id':reference,
'last_contact':last_contact,
'issue_document_reference': issue_document_reference}
message = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict': mapping_dict})
event = support_request.SupportRequest_getLastEvent(ticket_title)
if event is None:
support_request.notify(message_title=ticket_title, message=message)
support_request.Ticket_createProjectEvent(
ticket_title, 'outgoing', 'Web Message',
portal.service_module.slapos_crm_information.getRelativeUrl(),
text_content=description,
content_type='text/plain',
notification_message=notification_message_reference,
#language=XXX,
substitution_method_parameter_dict={
'compute_node_title':context.getTitle(),
'compute_node_id':reference,
'last_contact':last_contact,
'issue_document_reference': issue_document_reference
}
)
return support_request
portal = context.getPortalObject()
compute_node = context
now_date = DateTime()
......@@ -15,13 +14,4 @@ if message_dict.has_key('created_at'):
contact_date = DateTime(message_dict.get('created_at').encode('utf-8'))
return (now_date - contact_date) < maximum_days
# If no access status information, check in consumption report
for sale_packing_list in portal.portal_catalog(
portal_type="Sale Packing List Line",
simulation_state="delivered",
default_aggregate_uid=compute_node.getUid(),
sort_on=[('movement.start_date', 'DESC')],
limit=1):
return (now_date - sale_packing_list.getStartDate()) < maximum_days
return False
......@@ -96,8 +96,8 @@
<key> <string>left</string> </key>
<value>
<list>
<string>my_reference</string>
<string>my_title</string>
<string>my_reference</string>
</list>
</value>
</item>
......
......@@ -2,19 +2,20 @@ from Products.ERP5Type.Cache import CachingMethod
portal = context.getPortalObject()
def isSupportRequestCreationClosed(destination_decision=None):
limit = portal.portal_preferences.getPreferredSupportRequestCreationLimit(5)
limit = int(portal.portal_preferences.getPreferredSupportRequestCreationLimit(5))
kw = {}
kw['limit'] = limit
kw['portal_type'] = 'Support Request'
kw['simulation_state'] = ["validated","submitted"]
kw['default_resource_uid'] = portal.service_module.slapos_crm_monitoring.getUid()
kw = {
'limit': limit,
'portal_type': 'Support Request',
'simulation_state': ["validated", "submitted"],
'resource__uid': portal.service_module.slapos_crm_monitoring.getUid()
}
if destination_decision:
kw['default_destination_decision_uid'] = context.restrictedTraverse(
kw['destination_decision__uid'] = context.restrictedTraverse(
destination_decision).getUid()
support_request_amount = context.portal_catalog.countResults(**kw)[0][0]
return support_request_amount >= int(limit)
support_request_amount_list = context.portal_catalog(**kw)
return limit <= len(support_request_amount_list)
return CachingMethod(isSupportRequestCreationClosed,
......
portal = context.getPortalObject()
destination_decision_value = context
# Create a temp Sale Order to find the trade condition
now = DateTime()
module = portal.portal_trash
tmp_sale_order = module.newContent(
portal_type='Sale Order',
temp_object=True,
trade_condition_type="ticket",
start_date=now,
destination_value=destination_decision_value,
destination_decision_value=destination_decision_value,
source_project=source_project,
ledger_value=portal.portal_categories.ledger.automated
)
tmp_sale_order.SaleOrder_applySaleTradeCondition(batch_mode=1, force=1)
"""
if tmp_sale_order.getSpecialise(None) is None:
raise AssertionError('Can not find a trade condition to generate the Support Request')
"""
resource = portal.restrictedTraverse(resource)
ticket = portal.getDefaultModule(portal_type).newContent(
portal_type=portal_type,
title=title,
description=text_content,
start_date=tmp_sale_order.getStartDate(),
source=tmp_sale_order.getSource(),
source_section=tmp_sale_order.getSourceSection(),
source_project=tmp_sale_order.getSourceProject(),
destination=tmp_sale_order.getDestination(),
destination_section=tmp_sale_order.getDestinationSection(),
destination_project=tmp_sale_order.getDestinationProject(),
destination_decision=tmp_sale_order.getDestinationDecision(),
specialise=tmp_sale_order.getSpecialise(),
causality=causality,
# Ensure resoure is Monitoring
resource_value=resource,
quantity_unit=resource.getQuantityUnit(),
base_contribution_list=resource.getBaseContributionList(),
use=resource.getUse(),
quantity=1,
price=0
)
ticket.submit(comment=comment)
return ticket
......@@ -50,11 +50,11 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>instance, notification_message_reference</string> </value>
<value> <string>resource, title, text_content, portal_type=\'Support Request\', source_project=None, causality=None, comment=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>InstanceTree_createSupportRequestEvent</string> </value>
<value> <string>Entity_createTicketFromTradeCondition</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -4,10 +4,6 @@ from erp5.component.module.DateUtils import addToDate
instance_tree = context
portal = context.getPortalObject()
if instance_tree.getMonitorScope() == "disabled":
# Don't generate ticket if Monitor Scope is marked to disable
return
if portal.ERP5Site_isSupportRequestCreationClosed():
# Stop ticket creation
return
......@@ -20,14 +16,11 @@ if (date_check_limit - instance_tree.getCreationDate()) < 0:
software_instance_list = context.portal_catalog(
portal_type=["Software Instance", "Slave Instance"],
specialise_uid=instance_tree.getUid(),
specialise__uid=instance_tree.getUid(),
**{"slapos_item.slap_state": ["start_requested"]})
has_newest_allocated_instance = False
has_unallocated_instance = False
failing_instance = None
# Check if at least one software Instance is Allocated
notification_message_reference = None
for instance in software_instance_list:
if (date_check_limit - instance.getCreationDate()) < 0:
continue
......@@ -36,19 +29,48 @@ for instance in software_instance_list:
continue
compute_partition = instance.getAggregateValue()
if compute_partition is not None:
has_newest_allocated_instance = True
if instance.getPortalType() == "Software Instance" and \
compute_partition.getParentValue().getMonitorScope() == "enabled" and \
instance.SoftwareInstance_hasReportedError(tolerance=30):
return context.InstanceTree_createSupportRequestEvent(
instance, 'slapos-crm-instance-tree-instance-state.notification')
else:
has_unallocated_instance = True
failing_instance = instance
if has_unallocated_instance and has_newest_allocated_instance:
return context.InstanceTree_createSupportRequestEvent(
failing_instance, 'slapos-crm-instance-tree-instance-allocation.notification')
return
if compute_partition is None:
notification_message_reference = 'slapos-crm-instance-tree-instance-allocation.notification'
elif (instance.getPortalType() == "Software Instance") and \
(compute_partition.getParentValue().getMonitorScope() == "enabled") and \
instance.SoftwareInstance_hasReportedError(tolerance=30):
notification_message_reference = 'slapos-crm-instance-tree-instance-state.notification'
if notification_message_reference is not None:
ticket_title = "Instance Tree %s is failing." % context.getTitle()
error_message = instance.SoftwareInstance_hasReportedError(include_message=True)
description = "%s contains software instances which are unallocated or reporting errors." % (
context.getTitle())
if error_message:
description += "\n\nMessage: %s" % str(error_message)
else:
error_message = "No message!"
project = context.getFollowUpValue()
support_request = project.Project_createSupportRequestWithCausality(
ticket_title,
description,
causality=context.getRelativeUrl(),
destination_decision=context.getDestinationSection()
)
if support_request is None:
return
event = support_request.SupportRequest_getLastEvent(ticket_title)
if event is None:
support_request.Ticket_createProjectEvent(
ticket_title, 'outgoing', 'Web Message',
portal.service_module.slapos_crm_information.getRelativeUrl(),
text_content=description,
content_type='text/plain',
notification_message=notification_message_reference,
#language=XXX,
substitution_method_parameter_dict={
'instance_tree_title':context.getTitle(),
'instance': instance.getTitle(),
'error_text': error_message
}
)
return
portal = context.getPortalObject()
person = context.getDestinationSectionValue()
if person is None or portal.ERP5Site_isSupportRequestCreationClosed(person.getRelativeUrl()):
# Stop ticket creation
return
ticket_title = "Instance Tree %s is failing." % context.getTitle()
error_message = instance.SoftwareInstance_hasReportedError(include_message=True)
description = "%s contains software instances which are unallocated or reporting errors." % (
context.getTitle())
if error_message:
description += "\n\nMessage: %s" % error_message
else:
error_message = "No message!"
support_request = person.Base_getSupportRequestInProgress(
title=ticket_title,
aggregate=context.getRelativeUrl())
if support_request is None:
person.notify(support_request_title=ticket_title,
support_request_description=description,
aggregate=context.getRelativeUrl())
support_request_relative_url = context.REQUEST.get("support_request_relative_url")
if support_request_relative_url is None:
return
support_request = portal.restrictedTraverse(support_request_relative_url)
if support_request is None:
return
if support_request.getSimulationState() not in ["validated", "suspended"]:
support_request.validate()
# Send Notification message
message = description
notification_message = portal.portal_notifications.getDocumentValue(
reference=notification_message_reference)
if notification_message is not None:
mapping_dict = {'instance_tree_title':context.getTitle(),
'instance': instance.getTitle(),
'error_text': error_message}
message = notification_message.asText(
substitution_method_parameter_dict={'mapping_dict':mapping_dict})
event = support_request.SupportRequest_getLastEvent(ticket_title)
if event is None:
support_request.notify(message_title=ticket_title, message=message)
return context.REQUEST.get("ticket_notified_item")
from Products.ZSQLCatalog.SQLCatalog import ComplexQuery, SimpleQuery
portal = context.getPortalObject()
person_uid = context.getUid()
query = ComplexQuery(
ComplexQuery(
SimpleQuery(portal_type=["Support Request", "Regularisation Request"]),
SimpleQuery(simulation_state="suspended"),
SimpleQuery(destination_decision_uid=person_uid),
logical_operator='and'),
ComplexQuery(
SimpleQuery(portal_type="Upgrade Decision"),
SimpleQuery(simulation_state="confirmed"),
SimpleQuery(destination_decision_uid=person_uid),
logical_operator='and'),
logical_operator='or')
return portal.portal_catalog(query=query, **kw)
portal = context.getPortalObject()
pending_ticket_list_amount = len(context.Person_getSlapOSPendingTicket())
notification_message = portal.portal_notifications.getDocumentValue(
reference="slapos-crm-person-pending-ticket-notification")
if notification_message is not None:
mapping_dict = {'username': context.getTitle(),
'amount': pending_ticket_list_amount,
'website': portal.portal_preferences.getPreferredSlaposWebSiteUrl()}
return notification_message.getTitle(), notification_message.asText(
substitution_method_parameter_dict={'mapping_dict': mapping_dict})
message = """ You have %s pending tickets """ % pending_ticket_list_amount
return message, message
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_getSlapOSPendingTicketMessageTemplate</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
amount = len(context.Person_getSlapOSPendingTicket())
if amount > 0:
title, reminder_message = context.Person_getSlapOSPendingTicketMessageTemplate()
return context.Person_sendSlapOSPendingTicketNotification(
title,
reminder_message,
batch_mode=1
)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_sendPendingTicketReminder</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
portal = context.getPortalObject()
start_date = DateTime()
# Rely on Trade condition (like in ticket to set the proper sender)
trade_condition = portal.sale_trade_condition_module.slapos_ticket_trade_condition
event_kw = {
'portal_type' : "Mail Message",
'title' : response_event_title,
'resource' : "service_module/slapos_crm_information",
'source' : trade_condition.getSource(),
'destination' : context.getRelativeUrl(),
'start_date' : start_date,
'text_content' : response_event_text_content,
'content_type' : 'text/plain',
}
# Create event
event = portal.event_module.newContent(**event_kw)
event.start(send_mail=True, comment="Sent via Person_sendSlapOSPendingTicketNotification")
if batch_mode:
return event
message = portal.Base_translateString('New event created.')
return event.Base_redirect(keep_items={'portal_status_message': message})
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>response_event_title, response_event_text_content, batch_mode=False, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_sendSlapOSPendingTicketNotification</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>Person_sendSlapOSPendingTicketNotification</string> </value>
</item>
<item>
<key> <string>action_title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list>
<string>listbox</string>
</list>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list>
<string>your_response_event_title</string>
<string>your_response_event_text_content</string>
</list>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list>
<string>listbox_delivery_start_date</string>
</list>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>my_title</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_viewSlapOSPendingTicketDialog</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>Person_viewSlapOSPendingTicket</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_dialog</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Send Pending Ticket Report</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>columns</string>
<string>default_params</string>
<string>description</string>
<string>editable</string>
<string>editable_columns</string>
<string>list_method</string>
<string>portal_types</string>
<string>selection_name</string>
<string>sort</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>listbox</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>columns</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable_columns</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>list_method</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>portal_types</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>selection_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>sort</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>columns</string> </key>
<value>
<list>
<tuple>
<string>getTitle</string>
<string>Title</string>
</tuple>
<tuple>
<string>translated_portal_type</string>
<string>Event Type</string>
</tuple>
<tuple>
<string>delivery.start_date</string>
<string>Date</string>
</tuple>
<tuple>
<string>translated_simulation_state_title</string>
<string>State</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>default_params</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>List of all Support Requests related to the follow up ticket</string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>editable_columns</string> </key>
<value>
<list>
<tuple>
<string>delivery.start_date</string>
<string>Date</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_listbox</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>list_method</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>portal_types</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>selection_name</string> </key>
<value> <string>person_pending_ticket_view_selection</string> </value>
</item>
<item>
<key> <string>sort</string> </key>
<value>
<list>
<tuple>
<string>delivery.start_date</string>
<string>asc</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Tickets</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Method" module="Products.Formulator.MethodField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>method_name</string> </key>
<value> <string>Person_getSlapOSPendingTicket</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="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 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>**kw</string> </value>
<value> <string>title, text_content, causality, destination_decision</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_getSlapOSPendingTicket</string> </value>
<value> <string>Project_createSupportRequestWithCausality</string> </value>
</item>
</dictionary>
</pickle>
......
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