Commit d0f0cf5d authored by Romain Courteaud's avatar Romain Courteaud

Automate invoice cancellation.

Cancel invoice if user removes his instances.
parent 8de90af7
<?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_cancelInvoiceRelatedToSuspendedRegularisationRequest</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_crm_cancel_invoice</string> </value>
</item>
<item>
<key> <string>periodicity_hour</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_hour_frequency</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>periodicity_minute</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_minute_frequency</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>periodicity_month</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_month_day</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_start_date</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<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>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>Cancel Invoice</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<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>_body</string> </key>
<value> <string>portal = context.getPortalObject()\n
portal.portal_catalog.searchAndActivate(\n
portal_type="Regularisation Request", \n
simulation_state=["suspended"],\n
method_id=\'RegularisationRequest_cancelInvoiceIfPersonOpenOrderIsEmpty\',\n
activate_kw={\'tag\': tag}\n
)\n
context.activate(after_tag=tag).getId()\n
</string> </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_cancelInvoiceRelatedToSuspendedRegularisationRequest</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -92,6 +92,7 @@ if (ticket is None) and int(person.Entity_statBalance()) > 0:\n ...@@ -92,6 +92,7 @@ if (ticket is None) and int(person.Entity_statBalance()) > 0:\n
source_project_value=context,\n source_project_value=context,\n
title=\'Account regularisation expected for "%s"\' % context.getTitle(),\n title=\'Account regularisation expected for "%s"\' % context.getTitle(),\n
destination_decision_value=context,\n destination_decision_value=context,\n
destination_value=context,\n
start_date=DateTime(),\n start_date=DateTime(),\n
resource=portal.portal_preferences.getPreferredRegularisationRequestResource(),\n resource=portal.portal_preferences.getPreferredRegularisationRequestResource(),\n
)\n )\n
...@@ -100,17 +101,10 @@ if (ticket is None) and int(person.Entity_statBalance()) > 0:\n ...@@ -100,17 +101,10 @@ if (ticket is None) and int(person.Entity_statBalance()) > 0:\n
\n \n
ticket.reindexObject(activate_kw={\'tag\': tag})\n ticket.reindexObject(activate_kw={\'tag\': tag})\n
\n \n
# Inform user that the ticket has been created.\n mail_message = ticket.RegularisationRequest_checkToSendUniqEvent(\n
mail_message = portal.event_module.newContent(\n portal.portal_preferences.getPreferredRegularisationRequestResource(),\n
portal_type=\'Mail Message\',\n \'Invoice payment requested\',\n
start_date=DateTime(),\n """Dear user,\n
destination_value=person,\n
follow_up=ticket.getRelativeUrl(),\n
source_value=ticket.getSourceValue(),\n
title=\'Invoice payment requested\',\n
resource=ticket.getResource(),\n
text_content="""\n
Dear user,\n
\n \n
A new invoice has been generated. \n A new invoice has been generated. \n
You can access it in your invoice section at %s.\n You can access it in your invoice section at %s.\n
...@@ -119,10 +113,8 @@ Do not hesitate to visit the web forum (http://community.slapos.org/forum) in ca ...@@ -119,10 +113,8 @@ Do not hesitate to visit the web forum (http://community.slapos.org/forum) in ca
\n \n
Regards,\n Regards,\n
The slapos team\n The slapos team\n
""" % portal.portal_preferences.getPreferredSlaposWebSiteUrl())\n """ % portal.portal_preferences.getPreferredSlaposWebSiteUrl(),\n
portal.portal_workflow.doActionFor(mail_message, \'start_action\', send_mail=True, comment=\'Requested manual payment.\')\n \'Requested manual payment.\')\n
mail_message.stop(comment=\'Requested manual payment.\')\n
mail_message.deliver(comment=\'Requested manual payment.\')\n
\n \n
return ticket, mail_message\n return ticket, mail_message\n
\n \n
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<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>_body</string> </key>
<value> <string>from zExceptions import Unauthorized\n
if REQUEST is not None:\n
raise Unauthorized\n
\n
mail_message = None\n
invoice_list = []\n
\n
state = context.getSimulationState()\n
person = context.getSourceProjectValue(portal_type="Person")\n
if (state != \'suspended\') or \\\n
(person is None):\n
return mail_message, invoice_list\n
else:\n
portal = context.getPortalObject()\n
\n
open_order = portal.portal_catalog.getResultValue(\n
portal_type="Open Sale Order",\n
validation_state="validated",\n
default_destination_decision_uid=person.getUid())\n
\n
if (open_order is None) or \\\n
(open_order.getValidationState() != "validated") or \\\n
(len(open_order.contentValues(portal_type="Open Sale Order Line")) != 0):\n
return mail_message, invoice_list\n
else:\n
assert open_order.getDestinationDecisionUid() == person.getUid()\n
ticket = context\n
\n
for payment in portal.portal_catalog(\n
portal_type="Payment Transaction", \n
payment_mode_uid=portal.portal_categories.payment_mode.payzen.getUid(),\n
default_destination_section_uid=person.getUid(),\n
simulation_state=["started"],\n
):\n
\n
if payment.PaymentTransaction_getPayzenId()[1] is None:\n
invoice = payment.getCausalityValue(portal_type="Sale Invoice Transaction")\n
assert payment.getDestinationSectionUid() == person.getUid()\n
invoice.SaleInvoiceTransaction_createReversalPayzenTransaction()\n
invoice_list.append(invoice.getRelativeUrl())\n
\n
# XXX Hardcoded\n
cancel_service = portal.service_module.slapos_crm_invoice_cancellation\n
mail_message = ticket.RegularisationRequest_checkToSendUniqEvent(\n
cancel_service.getRelativeUrl(),\n
\'Cancellation of your bill\',\n
"""Hello,\n
\n
Thank you to have used our decentralized Cloud Computing service slapos.org.\n
\n
We noticed that all your instances have been removed upon receiving your bill, so we conclude that the instances that you requested were not being used but probably ordered then forgotten.\n
\n
To not to charge our first users a "non use" of our service, we have choosen to cancel your bill. That\'s mean: *You have nothing to pay us.*\n
\n
We hope to see you using our services in the future.\n
\n
Regards,\n
The slapos team\n
""",\n
\'Cancelled payment.\')\n
\n
return mail_message, invoice_list\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>RegularisationRequest_cancelInvoiceIfPersonOpenOrderIsEmpty</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<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>_body</string> </key>
<value> <string encoding="cdata"><![CDATA[
from zExceptions import Unauthorized\n
if REQUEST is not None:\n
raise Unauthorized\n
\n
portal = context.getPortalObject()\n
ticket = context\n
service = portal.restrictedTraverse(service_relative_url)\n
assert service.getPortalType() == "Service"\n
event_portal_type = "Mail Message"\n
\n
# XXX TODO\n
# # Prevent to create 2 tickets during the same transaction\n
# transactional_variable = getTransactionalVariable()\n
# if tag in transactional_variable:\n
# raise RuntimeError, \'ticket %s already exist\' % tag\n
# else:\n
# transactional_variable[tag] = None\n
\n
event = portal.portal_catalog.getResultValue(\n
portal_type=event_portal_type,\n
default_resource_uid=service.getUid(),\n
default_follow_up_uid=ticket.getUid(),\n
)\n
\n
if (event is None) and (ticket.getSimulationState() == \'suspended\'):\n
tag = "%s_addUniqEvent_%s" % (ticket.getUid(), service.getUid())\n
if (portal.portal_activities.countMessageWithTag(tag) > 0):\n
# The event is already under creation but can not be fetched from catalog\n
return None\n
\n
# Prevent concurrent transaction to create 2 events for the same ticket\n
ticket.edit(resource=service_relative_url)\n
\n
event = portal.event_module.newContent(\n
portal_type=event_portal_type,\n
start_date=DateTime(),\n
destination=ticket.getDestination(),\n
follow_up=ticket.getRelativeUrl(),\n
source=context.getSource(),\n
title=title,\n
resource=service_relative_url,\n
text_content=text_content,\n
)\n
\n
portal.portal_workflow.doActionFor(event, \'start_action\', send_mail=True, comment=comment)\n
event.stop(comment=comment)\n
event.deliver(comment=comment)\n
event.reindexObject(activate_kw={\'tag\': tag})\n
\n
return event\n
]]></string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>service_relative_url, title, text_content, comment, REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>RegularisationRequest_checkToSendUniqEvent</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -174,3 +174,67 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by R ...@@ -174,3 +174,67 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by R
self.assertEqual( self.assertEqual(
'Visited by RegularisationRequest_invalidateIfPersonBalanceIsOk', 'Visited by RegularisationRequest_invalidateIfPersonBalanceIsOk',
ticket.workflow_history['edit_workflow'][-1]['comment']) ticket.workflow_history['edit_workflow'][-1]['comment'])
class TestSlapOSCrmCancelInvoiceRelatedToSuspendedRegularisationRequest(testSlapOSMixin):
def beforeTearDown(self):
transaction.abort()
def createRegularisationRequest(self):
new_id = self.generateNewId()
return self.portal.regularisation_request_module.newContent(
portal_type='Regularisation Request',
title="Test Reg. Req.%s" % new_id,
reference="TESTREGREQ-%s" % new_id,
)
def _simulateRegularisationRequest_cancelInvoiceIfPersonOpenOrderIsEmpty(self):
script_name = 'RegularisationRequest_cancelInvoiceIfPersonOpenOrderIsEmpty'
if script_name in self.portal.portal_skins.custom.objectIds():
raise ValueError('Precondition failed: %s exists in custom' % script_name)
createZODBPythonScript(self.portal.portal_skins.custom,
script_name,
'*args, **kwargs',
'# Script body\n'
"""portal_workflow = context.portal_workflow
portal_workflow.doActionFor(context, action='edit_action', comment='Visited by RegularisationRequest_cancelInvoiceIfPersonOpenOrderIsEmpty') """ )
transaction.commit()
def _dropRegularisationRequest_cancelInvoiceIfPersonOpenOrderIsEmpty(self):
script_name = 'RegularisationRequest_cancelInvoiceIfPersonOpenOrderIsEmpty'
if script_name in self.portal.portal_skins.custom.objectIds():
self.portal.portal_skins.custom.manage_delObjects(script_name)
transaction.commit()
def test_alarm_not_suspended_regularisation_request(self):
ticket = self.createRegularisationRequest()
ticket.validate()
self.tic()
self._simulateRegularisationRequest_cancelInvoiceIfPersonOpenOrderIsEmpty()
try:
self.portal.portal_alarms.\
slapos_crm_cancel_invoice.activeSense()
self.tic()
finally:
self._dropRegularisationRequest_cancelInvoiceIfPersonOpenOrderIsEmpty()
self.assertNotEqual(
'Visited by RegularisationRequest_cancelInvoiceIfPersonOpenOrderIsEmpty',
ticket.workflow_history['edit_workflow'][-1]['comment'])
def test_alarm_suspended_regularisation_request(self):
ticket = self.createRegularisationRequest()
ticket.validate()
ticket.suspend()
self.tic()
self._simulateRegularisationRequest_cancelInvoiceIfPersonOpenOrderIsEmpty()
try:
self.portal.portal_alarms.\
slapos_crm_cancel_invoice.activeSense()
self.tic()
finally:
self._dropRegularisationRequest_cancelInvoiceIfPersonOpenOrderIsEmpty()
self.assertEqual(
'Visited by RegularisationRequest_cancelInvoiceIfPersonOpenOrderIsEmpty',
ticket.workflow_history['edit_workflow'][-1]['comment'])
12 13
\ No newline at end of file \ No newline at end of file
service_module/slapos_crm_acknowledgement service_module/slapos_crm_acknowledgement
service_module/slapos_crm_complaint service_module/slapos_crm_complaint
service_module/slapos_crm_information service_module/slapos_crm_information
service_module/slapos_crm_spam service_module/slapos_crm_spam
\ No newline at end of file service_module/slapos_crm_invoice_cancellation
\ No newline at end of file
...@@ -2,6 +2,7 @@ service_module/slapos_crm_acknowledgement ...@@ -2,6 +2,7 @@ service_module/slapos_crm_acknowledgement
service_module/slapos_crm_complaint service_module/slapos_crm_complaint
service_module/slapos_crm_information service_module/slapos_crm_information
service_module/slapos_crm_spam service_module/slapos_crm_spam
service_module/slapos_crm_invoice_cancellation
support_request_module/slapos_crm_support_request_template support_request_module/slapos_crm_support_request_template
regularisation_request_module/slapos_crm_regularisation_request_template regularisation_request_module/slapos_crm_regularisation_request_template
event_module/slapos_crm_web_message_template event_module/slapos_crm_web_message_template
\ No newline at end of file
event_module/slapos_crm_web_message_template event_module/slapos_crm_web_message_template
portal_alarms/slapos_crm_cancel_invoice
portal_alarms/slapos_crm_create_regularisation_request portal_alarms/slapos_crm_create_regularisation_request
portal_alarms/slapos_crm_invalidate_suspended_regularisation_request portal_alarms/slapos_crm_invalidate_suspended_regularisation_request
regularisation_request_module/slapos_crm_regularisation_request_template regularisation_request_module/slapos_crm_regularisation_request_template
service_module/slapos_crm_acknowledgement service_module/slapos_crm_acknowledgement
service_module/slapos_crm_complaint service_module/slapos_crm_complaint
service_module/slapos_crm_information service_module/slapos_crm_information
service_module/slapos_crm_invoice_cancellation
service_module/slapos_crm_spam service_module/slapos_crm_spam
support_request_module/slapos_crm_support_request_template support_request_module/slapos_crm_support_request_template
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment