Commit 8418eeb0 authored by Romain Courteaud's avatar Romain Courteaud

slapos_subscription_request:

* test that Subscription Request is created from Compute Node
* drop SubscriptionRequest_processStopped
* drop SubscriptionRequest_processStarted
* drop SubscriptionRequest_processOrdered
* drop SubscriptionRequest_processConfirmed
* drop SubscriptionRequest_processRequest
* drop SubscriptionCondition_renderKVMClusterParameter
* drop Base_instanceXmlToDict
* drop SubscriptionCondition_renderParameter
* drop testSlapOSERP5VirtualMasterSubscriptionRequestScenario
* ensure Subscription Request is consistent
* allow to choose the currency when creating a Subscription Request
* try to close the Subscription Request as soon as user paid an invoice
* block subscription process if there is no deposit
* do not accept new services if the virtual master subscription state is not expected
* use Base_reindexAndSenseAlarm
parent 4144073e
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from lxml import etree
from zExceptions import Unauthorized
from zLOG import LOG, INFO
def SubscriptionRequest_saveTransactionalUser(self, person=None):
if person.getPortalType() == "Person":
......@@ -14,31 +12,6 @@ def SubscriptionRequest_getTransactionalUser(self, REQUEST=None):
raise Unauthorized
return getTransactionalVariable().get("transactional_user", None)
def Base_instanceXmlToDict(self, xml):
result_dict = {}
try:
if xml is not None and xml != '':
tree = etree.fromstring(xml)
for element in tree.findall('parameter'):
key = element.get('id')
value = result_dict.get(key, None)
if value is not None:
value = value + ' ' + element.text
else:
value = element.text
result_dict[key] = value
except (etree.XMLSchemaError, etree.XMLSchemaParseError, #pylint: disable=catching-non-exception
etree.XMLSchemaValidateError, etree.XMLSyntaxError): #pylint: disable=catching-non-exception
LOG('SubscriptionRequest', INFO, 'Issue during parsing xml:', error=True)
return result_dict
def SubscriptionCondition_renderParameter(self, amount=0, **kw):
method_id = self.getParameterTemplateRendererMethodId()
if method_id is not None:
return getattr(self, method_id)(amount=amount, **kw)
return self.getTextContent()
def SubscriptionRequest_searchExistingUserByEmail(self, email, REQUEST=None):
if REQUEST is not None:
raise Unauthorized
......
......@@ -6,12 +6,6 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>SlapOSSubscriptionRequest</string> </value>
......@@ -55,28 +49,13 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......@@ -89,7 +68,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -98,7 +77,7 @@
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
......
......@@ -11,6 +11,10 @@
<type>Instance Tree</type>
<workflow>slapos_subscription_request_interaction_workflow</workflow>
</chain>
<chain>
<type>Sale Invoice Transaction Line</type>
<workflow>slapos_subscription_request_interaction_workflow</workflow>
</chain>
<chain>
<type>Subscription Condition</type>
<workflow>commerce_validation_workflow, edit_workflow</workflow>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>Base_instanceXmlToDict</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>SlapOSSubscriptionRequest</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_instanceXmlToDict</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -49,12 +49,17 @@ tmp_sale_order = module.newContent(
source_project_value=source_project_value,
ledger_value=portal.portal_categories.ledger.automated,
# XXX XXX destination_project_value=instance_tree.getFollowUpValue(),
price_currency_value=currency_value
)
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 Subscription Request')
if currency_value is not None:
if currency_value.getRelativeUrl() != tmp_sale_order.getPriceCurrency():
raise AssertionError('Unexpected different currency: %s %s' % (currency_value.getRelativeUrl(), tmp_sale_order.getPriceCurrency()))
# If no accounting is needed, no need to check the price
if (tmp_sale_order.getSourceSection(None) == tmp_sale_order.getDestinationSection(None)) or \
(tmp_sale_order.getSourceSection(None) is None):
......@@ -118,10 +123,9 @@ subscription_request = portal.subscription_request_module.newContent(
price=price,
# XXX activate_kw=activate_kw
)
"""
if len(subscription_request.checkConsistency()) != 0:
raise NotImplementedError(subscription_request.checkConsistency())
"""
raise AssertionError(subscription_request.checkConsistency())
subscription_request.submit()
return subscription_request
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>subscriber_person_value, variation_category_list, project_value</string> </value>
<value> <string>subscriber_person_value, variation_category_list, project_value, currency_value=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
"""
Take the Text Content and adapt it as template for create
an variation for a specific software release.
This adapt the definition of KVM to instantiate multiple VMs instead a single
for example.
"""
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
import json
parameter_text = context.getTextContent()
if not amount or parameter_text is None:
return None
json_parameter = context.Base_instanceXmlToDict(parameter_text)
if "_" in json_parameter:
# This is json-in-xml serialisation
json_parameter = json.loads(json_parameter["_"])
else:
raise ValueError("KVM Cluster only supports serialised values!")
KVM0 = json_parameter["kvm-partition-dict"]["KVM0"]
for i in range(amount):
if i == 0:
k = KVM0.copy()
k["sticky-compute-node"] = True
json_parameter["kvm-partition-dict"]["KVM" + str(i)] = k
else:
json_parameter["kvm-partition-dict"]["KVM" + str(i)] = KVM0
xml_parameter = """<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="_">%s</parameter>
</instance>""" % json.dumps(json_parameter, indent=2)
return xml_parameter
<?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>amount=0, REQUEST=None, **kw</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SubscriptionCondition_renderKVMClusterParameter</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>SubscriptionCondition_renderParameter</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>SlapOSSubscriptionRequest</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SubscriptionCondition_renderParameter</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
instance_tree = context.getAggregateValue()
if instance_tree is None:
# Probably we should raise here
return
# Instance is already destroyed so move into stopped state diretly.
if instance_tree.getValidationState() == "archived":
comment="Instance Tree is Destroyed and archived, Stop the Subscription Request"
context.start(comment=comment)
context.stop(comment=comment)
return comment
request_kw = dict(
software_release=instance_tree.getUrlString(),
software_title=instance_tree.getTitle(),
software_type=instance_tree.getSourceReference(),
instance_xml=instance_tree.getTextContent(),
sla_xml=instance_tree.getSlaXml(),
shared=instance_tree.isRootSlave(),
project_reference=instance_tree.getFollowUpReference()
)
if not context.SubscriptionRequest_testPaymentBalance():
# Payment isn't paid by the user, so we stop the instance and wait
if instance_tree.getSlapState() == "start_requested":
person = instance_tree.getDefaultDestinationSectionValue()
person.requestSoftwareInstance(state='stopped', **request_kw)
return "Skipped (Payment is pending)"
if instance_tree.getSlapState() == "stop_requested":
person = instance_tree.getDefaultDestinationSectionValue()
person.requestSoftwareInstance(state='started', **request_kw)
# Return to because it is useless continue right the way.
return "Skipped (Started instance)"
if not context.SubscriptionRequest_verifyInstanceIsAllocated():
# Only continue if instance is ready
return "Skipped (Instance is failing)"
if context.SubscriptionRequest_notifyInstanceIsReady():
context.start(comment="Instance is ready")
return "Instance is ready"
return "Skipped (Instance isn't ready)"
<?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>SubscriptionRequest_processConfirmed</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
if context.getSimulationState() != "ordered":
# Skip if the instance isn't ordered anymore
return
instance_tree = context.getAggregateValue()
# Don't request again if it is already requested.
if instance_tree is None:
context.SubscriptionRequest_processRequest()
# Don't perform everything on the same transaction
return "Skipped (Instance Requested)"
if instance_tree is not None:
if instance_tree.getCausalityState() == "diverged":
# Call it as soon as possible
subscription_condition = context.getSpecialiseValue(portal_type='Subscription Condition')
trade_condition = subscription_condition.getSpecialiseValue(portal_type='Sale Trade Condition')
if trade_condition is None:
return 'Skipped (No Trade Condition)'
instance_tree.InstanceTree_requestUpdateOpenSaleOrder(trade_condition.getRelativeUrl())
instance = instance_tree.getSuccessorValue()
# This ensure that the user has a valid cloud contract.
# At this stage he already have a paied invoice for the reservation,
# so the cloud contact will be just created.
instance.SoftwareInstance_requestValidationPayment()
# create a Deduction for his fee
context.SubscriptionRequest_generateReservationRefoundSalePackingList()
# Instance is already destroyed so move into stopped state diretly.
if instance_tree.getValidationState() == "archived":
comment="Instance Tree is Destroyed and archived, Stop the Subscription Request"
context.confirm(comment=comment)
context.start(comment=comment)
context.stop(comment=comment)
invoice = context.SubscriptionRequest_verifyPaymentBalanceIsReady()
if not invoice:
# Invoice isn't available for the user to Pay
return "Skipped (Payment isn't ready)"
if not context.SubscriptionRequest_verifyInstanceIsAllocated():
# Only continue if instance is ready
return "Skipped (Instance isn't ready)"
# Link to be sent is the invoice one
if context.SubscriptionRequest_notifyPaymentIsReady(invoice):
context.confirm(comment="Payment is ready for the user")
return "Payment is ready for the user"
return "Skipped (User isn't notified)"
<?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>SubscriptionRequest_processOrdered</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
if context.getAggregate() is not None:
return
subscription_condition = context.getSpecialiseValue(portal_type='Subscription Condition')
if subscription_condition is None:
return
person = context.getDestinationSectionValue()
if person is None:
return
if context.getSimulationState() == "confirmed":
return
request_kw = {}
default_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance>
</instance>
"""
if subscription_condition.getUrlString() is None:
raise ValueError("url_string cannot be None")
request_kw.update(
software_release=subscription_condition.getUrlString(),
# Bad title
software_title=context.getTitle() + " %s" % str(context.getUid()),
software_type=subscription_condition.getSourceReference("default"),
instance_xml = (subscription_condition.SubscriptionCondition_renderParameter(
amount=int(context.getQuantity())
) or default_xml).strip(),
sla_xml=subscription_condition.getSlaXml(default_xml).strip(),
shared=bool(subscription_condition.getRootSlave(0)),
state="started",
project_reference=subscription_condition.getFollowUpReference()
)
person.requestSoftwareInstance(**request_kw)
requested_software_instance = context.REQUEST.get('request_instance')
if requested_software_instance is None:
return
# Save the requested instance tree
context.setAggregate(requested_software_instance.getSpecialise())
<?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>SubscriptionRequest_processRequest</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
instance_tree = context.getAggregateValue()
if instance_tree is not None and \
instance_tree.getSlapState() == "destroy_requested":
context.stop(comment="Instance is Destroyed")
<?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>SubscriptionRequest_processStarted</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# Search by SPL for the first period
portal = context.getPortalObject()
# This is normally one, but we navegate in case
for packing_list in portal.portal_catalog(
portal_type="Sale Packing List",
causality_uid=context.getUid(),
specialise_uid=portal.restrictedTraverse(
portal.portal_preferences.getPreferredAggregatedSubscriptionSaleTradeCondition()).getUid(),
):
for invoice in packing_list.getCausalityRelatedValueList(
portal_type="Sale Invoice Transaction"):
for payment in invoice.getCausalityRelatedValueList(
portal_type=["Payment Transaction", "Sale Invoice Transaction"]):
if payment.getSimulationState() not in ["confirmed", "started"]:
if round(payment.PaymentTransaction_getTotalPayablePrice(), 2) == 0.0:
continue
return
# No open Payments pending.
context.deliver()
<?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>SubscriptionRequest_processStopped</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -8,6 +8,55 @@ if item is None:
# Use list setter, to ensure it crashes if item is still None
# subscription_request.setAggregateValueList([item])
# If the virtual master is not in the expected subscription status,
# do not accept any new service (compute node, instance) for it
if (((subscription_request.getSourceProjectValue() is not None) and
(subscription_request.getSourceProjectValue().Item_getSubscriptionStatus() != 'subscribed')) or
((subscription_request.getDestinationProjectValue() is not None) and
(subscription_request.getDestinationProjectValue().Item_getSubscriptionStatus() != 'subscribed'))):
return
# Accept the subscription only if user paid the security payment
if (subscription_request.getSourceValue() is None) or \
(subscription_request.getSource() != subscription_request.getDestination()):
# Check that user has enough guarantee deposit to request a new service
portal = context.getPortalObject()
assert_price_kw = {
'resource_uid': subscription_request.getPriceCurrencyUid(),
'portal_type': portal.getPortalAccountingMovementTypeList(),
}
deposit_amount = portal.portal_simulation.getInventoryAssetPrice(
section_uid= subscription_request.getSourceSectionUid(),
mirror_section_uid= subscription_request.getDestinationSectionUid(),
mirror_node_uid=portal.restrictedTraverse('account_module/deposit').getUid(),
#node_category_strict_membership=['account_type/income'],
simulation_state= ('stopped', 'delivered'),
#src__=1,
**assert_price_kw
)
#return deposit_amount
payable_amount = portal.portal_simulation.getInventoryAssetPrice(
mirror_section_uid= subscription_request.getSourceSectionUid(),
section_uid= subscription_request.getDestinationSectionUid(),
node_category_strict_membership=['account_type/asset/receivable',
'account_type/liability/payable'],
grouping_reference=None,
**assert_price_kw
)
total_price = subscription_request.getTotalPrice()
# XXX what is the guarantee deposit account_type?
if deposit_amount < payable_amount + total_price:
# if not enough, user will have to pay a deposit for the subscription
# XXX probably create an event asking for a deposit
#pass
return 'NO deposit_amount %s\npayable_amount %s\ntotal_price %s' % (deposit_amount, payable_amount, total_price)
#return 'YES deposit_amount %s\npayable_amount %s\ntotal_price %s' % (deposit_amount, payable_amount, total_price)
subscription_request.SubscriptionRequest_createOpenSaleOrder()
subscription_request.validate()
subscription_request.invalidate()
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2022 Nexedi SA and Contributors. All Rights Reserved.
#
##############################################################################
from erp5.component.test.testSlapOSERP5VirtualMasterScenario import TestSlapOSVirtualMasterScenarioMixin
from DateTime import DateTime
class TestSlapOSVirtualMasterSubscriptionRequestScenario(TestSlapOSVirtualMasterScenarioMixin):
"""
def createAccountableProject(self):
# create a default project
project = self.addProject(is_accountable=True)
self.web_site = self.portal.web_site_module.slapos_master_panel
preference = self.portal.portal_preferences.slapos_default_system_preference
preference.edit(
preferred_subscription_assignment_category_list=[
'function/customer',
'role/client',
'destination_project/%s' % project.getRelativeUrl()
]
)
return project, self.web_site
"""
def createProjectAdministrator(self, project, web_site):
self.logout()
login = 'owner-%s' % self.generateNewId()
self.joinSlapOS(web_site, login)
self.login()
person = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=login).getParentValue()
# first slapos administrator assignment can only be created by
# the erp5 manager
self.addProjectProductionManagerAssignment(person, project)
self.tic()
return person
def createSoftwareProduct(self, project, person):
self.logout()
self.login(person.getUserId())
software_url = self.generateNewSoftwareReleaseUrl()
software_type = 'public type'
return self.addSoftwareProduct(
"instance product", project, software_url, software_type
)
def createPreparedComputeNode(self, project, person, software_product,
release_variation, type_variation):
self.logout()
self.login(person.getUserId())
server_title = 'Server for %s' % project.getReference()
server_id = self.requestComputeNode(server_title, project.getReference())
server = self.portal.portal_catalog.getResultValue(
portal_type='Compute Node', reference=server_id)
"""
self.setAccessToMemcached(server)
self.assertNotEqual(None, server)
self.setServerOpenPublic(server)"""
self.addAllocationSupply("for compute node", server, software_product,
release_variation, type_variation)
# and install some software on them
self.supplySoftware(server, release_variation.getUrlString())
# format the compute_nodes
self.formatComputeNode(server)
return server
def createSoftwareProductSaleSupply(self, software_product, price=9):
self.logout()
# XXX Use accountant account
self.login()
project = software_product.getFollowUpValue()
sale_supply = self.portal.sale_supply_module.newContent(
portal_type="Sale Supply",
destination_project_value=project,
price_currency_value=project.getSpecialiseValue().getPriceCurrencyValue()
)
# XXX Put price in sale supply module
sale_supply.newContent(
portal_type="Sale Supply Line",
base_price=price,
resource_value=software_product
)
sale_supply.validate()
def addSaleManagerAssignment(self, person):
person.newContent(
portal_type='Assignment',
function='function/sale/manager'
).open()
def createSaleAdministrator(self, web_site):
self.logout()
login = 'sale-%s' % self.generateNewId()
self.joinSlapOS(web_site, login)
self.login()
person = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=login).getParentValue()
# first slapos administrator assignment can only be created by
# the erp5 manager
self.addSaleManagerAssignment(person)
# Remove customer project assignment
person.manage_delObjects(ids=[x.getId() for x in person.contentValues(portal_type="Assignment") if x.getFunction() == 'customer'])
self.tic()
return person
def addAccountingManagerAssignment(self, person):
person.newContent(
portal_type='Assignment',
function='function/accounting/manager'
).open()
def createAccountingAdministrator(self, web_site):
self.logout()
login = 'accounting-%s' % self.generateNewId()
self.joinSlapOS(web_site, login)
self.login()
person = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=login).getParentValue()
# first slapos administrator assignment can only be created by
# the erp5 manager
self.addAccountingManagerAssignment(person)
# Remove customer project assignment
person.manage_delObjects(ids=[x.getId() for x in person.contentValues(portal_type="Assignment") if x.getFunction() == 'customer'])
self.tic()
return person
def createCustomer(self, web_site):
self.logout()
login = 'customer-%s' % self.generateNewId()
self.joinSlapOS(web_site, login)
self.login()
person = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=login).getParentValue()
self.tic()
return person
"""
def bootstrapInvoicingScenario(self):
# Done by ERP5 admin?
project, web_site = self.createAccountableProject()
# lets join as slapos administrator, which will own few compute_nodes
owner_person = self.createProjectAdministrator(project, web_site)
# create a software product
software_product, release_variation, type_variation = self.createSoftwareProduct(project, owner_person)
# create compute_nodes and prepare it
compute_node = self.createPreparedComputeNode(project, owner_person, software_product,
release_variation, type_variation)
# define price
self.createSoftwareProductSaleSupply(software_product)
return project, web_site, software_product, release_variation, type_variation, compute_node
"""
def createProjectCustomer(self, web_site):
# join as the another visitor and request software instance on public
# compute_node
self.logout()
login = 'customer-%s' % self.generateNewId()
self.joinSlapOS(web_site, login)
self.login()
person = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=login).getParentValue()
return person, login
def bootstrapSubscriptionRequestScenario(self):
# Done by ERP5 admin?
# XXX XXX to drop. First project is created on UI
self.web_site = self.portal.web_site_module.slapos_master_panel
web_site = self.web_site
sale_person = self.createSaleAdministrator(web_site)
accountant_person = self.createAccountingAdministrator(web_site)
#################################
# Create accounting data
self.logout()
self.login(accountant_person.getUserId())
currency = self.portal.currency_module.newContent(
portal_type='Currency',
reference="test-currency-%s" % self.generateNewId(),
short_title="tc%s" % self.generateNewId(),
base_unit_quantity=0.01
)
currency.validate()
#################################
# Prepare providing free virtual master to sale users
self.logout()
self.login(sale_person.getUserId())
now = DateTime()
seller_organisation = self.portal.organisation_module.newContent(
portal_type="Organisation",
title="test-seller-%s" % self.generateNewId()
)
seller_bank_account = seller_organisation.newContent(
portal_type="Bank Account",
title="test_bank_account_%s" % self.generateNewId()
)
seller_organisation.validate()
sale_trade_condition = self.portal.sale_trade_condition_module.newContent(
portal_type="Sale Trade Condition",
reference='seller_organisation_%s' % seller_organisation.getTitle(),
# XXX hardcoded
specialise="business_process_module/slapos_ultimate_business_process",
source_value=seller_organisation,
source_section_value=seller_organisation,
source_payment_value=seller_bank_account,
price_currency_value=currency,
payment_condition_payment_mode='test-%s' % self.generateNewId()
)
sale_trade_condition.newContent(
portal_type="Trade Model Line",
reference="VAT",
resource="service_module/slapos_tax",
base_application="base_amount/invoicing/taxable",
trade_phase="slapos/tax",
price=0.2,
quantity=1.0,
membership_criterion_base_category=('price_currency',),
membership_criterion_category=('price_currency/%s' % currency.getRelativeUrl(),)
)
sale_trade_condition.validate()
sale_person.setCareerSubordinationValue(seller_organisation)
internal_trade_condition = self.portal.sale_trade_condition_module.newContent(
portal_type="Sale Trade Condition",
reference='internal_for_%s' % seller_organisation.getTitle(),
# XXX hardcoded
specialise_value=sale_trade_condition,
destination_section_value=seller_organisation,
effective_date=now.earliestTime()
)
internal_trade_condition.validate()
self.tic()
return web_site, now, currency, sale_person, seller_organisation, internal_trade_condition
def checkSiteConsistency(self):
self.logout()
for _ in range(20):
self.stepCallAlarmList()
self.tic()
self.login()
self.stepcheckERP5Consistency()
# after accept, an email is send containing the reset link
last_message = self.portal.MailHost._last_message
assert last_message is None, last_message
def checkVirtualMasterSubscription(self, user_person, now):
#################################
# Buy the first project
self.logout()
self.login(user_person.getUserId())
# XXX How to list possible service to sell?
# Search product with `time` unit ?
service = self.portal.restrictedTraverse('service_module/slapos_virtual_master_subscription')
# XXX This is a script like: Product_submitSubscriptionRequest()
source_decision_value = self.portal.portal_membership.getAuthenticatedMember().getUserValue()
source_section_value = source_decision_value.getCareerSubordinationValue(source_decision_value)
source_section_value = source_decision_value.getCareerSubordinationValue(source_decision_value)
# Find trade condition / price
order_portal_type = 'Sale Order'
line_portal_type = 'Sale Order Line'
#cell_portal_type = 'Sale Order Cell'
#base_id = 'movement'
module = self.portal.sale_order_module
temp_order = 1
#aggregate_value_list = []
open_sale_order = module.newContent(
portal_type=order_portal_type,
temp_object=temp_order,
#effective_date=now+1,
start_date=now,
# Ensure stop date value is higher than start date
# it will be updated by OpenSaleOrder_updatePeriod
# stop_date=now + 2,
destination_value=source_decision_value,
destination_section_value=source_section_value,
#destination_decision_value=source_decision_value,
ledger_value=self.portal.portal_categories.ledger.automated,
# XXX XXX destination_project_value=instance_tree.getFollowUpValue(),
)
"""
resource_vcl = [
'software_release/%s' % software_release.getRelativeUrl(),
'software_type/%s' % software_type.getRelativeUrl()
]
resource_vcl.sort()
assert len(resource_vcl) == 2, service
"""
# Add lines
open_order_line = open_sale_order.newContent(
portal_type=line_portal_type,
temp_object=temp_order,
resource_value=service,
#variation_category_list=resource_vcl,
quantity_unit=service.getQuantityUnit(),
base_contribution_list=service.getBaseContributionList(),
use=service.getUse(),
# stop_date=calculateOpenOrderLineStopDate(open_sale_order_line,
# instance_tree, start_date_delta=start_date_delta),
#activate_kw=activate_kw
quantity=1
)
"""
subscription_request = instance_tree.getAggregateRelatedValue(portal_type="Subscription Request")
# Define the start date of the period, this can variates with the time.
# start_date_delta = 0
if subscription_request is not None:
# Quantity is double because the first invoice has to
# charge for 2 months
edit_kw['quantity'] = subscription_request.getQuantity()
"""
"""
cell_key = list(open_order_line.getCellKeyList(base_id=base_id))[0]
open_order_cell = open_order_line.newCell(
base_id=base_id,
portal_type=cell_portal_type,
temp_object=temp_order,
*cell_key
)
open_order_cell.edit(
mapped_value_property_list=['price','quantity'],
quantity=1,
predicate_category_list=cell_key,
variation_category_list=cell_key,
aggregate_value_list=aggregate_value_list,
activate_kw=activate_kw
)
"""
open_sale_order.SaleOrder_applySaleTradeCondition(batch_mode=1, force=1)
subscription_request = self.portal.subscription_request_module.newContent(
portal_type='Subscription Request',
source_value=source_decision_value,
source_section_value=source_section_value,
source_decision_value=source_decision_value,
start_date=now,
effective_date=now,
resource_value=service,
quantity_unit=open_order_line.getQuantityUnit(),
quantity=1,
ledger="automated",
specialise_value=open_sale_order.getSpecialiseValue(),
destination=open_sale_order.getSource(),
destination_section=open_sale_order.getSourceSection(),
price_currency=open_sale_order.getPriceCurrency(),
price=open_order_line.getPrice()
)
"""
invoicing_type=invoicing_type,
number_tag=number_tag,
number_clip=number_clip,
insured_vehicle_count=insured_vehicle_count,
license_plate_number_list=license_plate_number_list,
free_subscription_dict_json=dumps(free_subscription_dict_json),
promotion_code=promotion_code,
member_reference=member_reference,
role=role,
company_title=company_title,
vat_code=vat_code,
social_title=social_title,
first_name=first_name,
last_name=last_name,
date_of_birth=date_of_birth,
default_email_text=default_email_text,
mobile_telephone_telephone_country=mobile_telephone_telephone_country,
mobile_telephone_telephone_number=mobile_telephone_text,
default_telephone_telephone_country=default_telephone_telephone_country,
default_address_zip_code=default_address_zip_code,
default_address_street_address='\n'.join((default_address_text2, default_address_text3, default_address_text1, default_address_text4)),
default_address_city=default_address_city,
default_address_region=default_address_region or None,
language=language,
client_address=request.getClientAddr(),
destination_reference=destination_reference,
accept_opteven_info=accept_opteven_info,
delivery_mode=delivery_mode,
price_list_json=dumps(price_list),
source_reference=tag_reference, # to lookup by catalog
pack_reference=pack_reference,
# Following fields are hidden and autocompleted by DQE result.
# So it is not passed in the parameters and we read it be REQUEST form
default_address_autocompleted=bool(int(request_form.get('field_your_default_address_autocompleted', "0"))),
delivery_address_autocompleted=has_delivery_address and bool(int(request_form.get('field_your_delivery_address_autocompleted', "0"))),
)
"""
if len(subscription_request.checkConsistency()) != 0:
self.tic()
raise NotImplementedError(str(subscription_request.checkConsistency()[0]))
subscription_request.submit()
self.tic()
#payment_transaction = None
self.logout()
self.login()
#################################
# Alarm / Sale Person validate the request
#################################
item = self.portal.project_module.newContent(
portal_type="Project",
title="new title",
)
item.validate()
subscription_trade_condition = subscription_request.getSpecialiseValue()
project_trade_condition = self.portal.sale_trade_condition_module.newContent(
portal_type="Sale Trade Condition",
reference='for_project_%s' % item.getTitle(),
# XXX hardcoded
specialise="business_process_module/slapos_ultimate_business_process",
source_value=subscription_trade_condition.getSourceValue(),
source_section_value=subscription_trade_condition.getSourceSectionValue(),
destination_project_value=item,
effective_date=now.earliestTime(),
price_currency_value=subscription_trade_condition.getPriceCurrencyValue()
)
project_trade_condition.validate()
subscription_request.getSourceDecisionValue().newContent(
portal_type="Assignment",
destination_project_value=item,
function='production/manager'
).open()
hosting_subscription = self.portal.hosting_subscription_module.newContent(
portal_type="Hosting Subscription",
title="hosting %s" % item.getTitle(),
#follow_up_value=instance_tree.getFollowUpValue(),
ledger_value=self.portal.portal_categories.ledger.automated,
)
hosting_subscription.validate()
start_date = hosting_subscription.HostingSubscription_calculateSubscriptionStartDate()
open_sale_order = self.portal.open_sale_order_module.newContent(
portal_type="Open Sale Order",
start_date=start_date,
# Ensure stop date value is higher than start date
# it will be updated by OpenSaleOrder_updatePeriod
stop_date=start_date + 1,
specialise_value=subscription_request.getSpecialiseValue(),
destination_value=subscription_request.getSourceValue(),
destination_section_value=subscription_request.getSourceSectionValue(),
destination_decision_value=subscription_request.getSourceDecisionValue(),
ledger_value=self.portal.portal_categories.ledger.automated,
causality_value=subscription_request
)
open_sale_order.SaleOrder_applySaleTradeCondition(batch_mode=1)
# Add lines
service = subscription_request.getResourceValue()
open_order_line = open_sale_order.newContent(
portal_type="Open Sale Order Line",
resource_value=service,
#variation_category_list=resource_vcl,
quantity_unit_value=subscription_request.getQuantityUnitValue(),
base_contribution_list=service.getBaseContributionList(),
use=service.getUse(),
# stop_date=calculateOpenOrderLineStopDate(open_sale_order_line,
# instance_tree, start_date_delta=start_date_delta),
#activate_kw=activate_kw
quantity=1,
price=subscription_request.getPrice(),
aggregate_value_list=[
hosting_subscription,
item
]
)
open_sale_order.plan()
open_sale_order.validate()
subscription_request.validate()
"""
payment_transaction = self.portal.accounting_module.newContent(
portal_type='Payment Transaction',
#start_date=context.getStopDate(),
#resource='currency_module/EUR',
#source_section=sanef_fr_relative_url,
#source_payment_value=bank_account,
causality_value=subscription_request,
#specialise_value=direct_debit_mandate,
# destination_section_value=customer,
#destination_payment=direct_debit_mandate.getSourcePayment(),
#payment_mode='direct_debit',
ledger="automated",
created_by_builder=1, # XXX this prevent init script from creating lines.
#activate_kw={'tag':'%s_init' % context.getDestinationReference()}
)
"""
"""
getAccountForUse = context.Base_getAccountForUse
# receivable
payment_transaction.newContent(
id='receivable',
portal_type='Accounting Transaction Line',
quantity=total_price,
source_value=getAccountForUse('asset_receivable_subscriber'),
)
# bank
payment_transaction.newContent(
id='bank',
portal_type='Accounting Transaction Line',
quantity=-total_price,
source_value=getAccountForUse('collection'),
aggregate_value=payment_transaction_group_value,
)
tag = '%s_update' % context.getDestinationReference()
payment_transaction.confirm(activate_kw={'tag': tag})
payment_transaction.activate(after_tag=tag).stop()
"""
#assert payment_transaction is not None
#################################
# And finally, allow customer to buy a virtual master
#################################
# virtual master subscription is NOT PUBLIC (too complex for anonymous?)
"""
customer, _ = self.createProjectCustomer(web_site)
self.logout()
self.login(customer.getUserId())
"""
return subscription_request
def test_virtual_master_sale_subscribe_to_project(self):
_, now, _, sale_person, _, _ = self.bootstrapSubscriptionRequestScenario()
self.tic()
self.checkVirtualMasterSubscription(sale_person, now)
self.checkSiteConsistency()
def test_virtual_master_customer_subscribe_to_project(self):
web_site, now, _, sale_person, _, internal_trade_condition = self.bootstrapSubscriptionRequestScenario()
self.tic()
main_project_subscription = self.checkVirtualMasterSubscription(sale_person, now)
self.logout()
self.login()
self.tic()
project = main_project_subscription.getCausalityRelatedValue(portal_type="Open Sale Order")\
.contentValues(portal_type="Open Sale Order Line")[0]\
.getAggregateValue(portal_type="Project")
# Setup preferences
preference = self.portal.portal_preferences.slapos_default_system_preference
preference.edit(
preferred_subscription_assignment_category_list=[
'function/customer',
'role/client',
'destination_project/%s' % project.getRelativeUrl()
]
)
#################################
# Buy the first project
customer_person = self.createCustomer(web_site)
self.logout()
self.login(sale_person.getUserId())
user_trade_condition = self.portal.sale_trade_condition_module.newContent(
portal_type="Sale Trade Condition",
reference='user_for_%s' % customer_person.getTitle(),
# XXX hardcoded
specialise_value=internal_trade_condition.getSpecialiseValue(),
#source_value=seller_organisation,
#source_section_value=seller_organisation,
destination_value=customer_person,
destination_section_value=customer_person,
effective_date=now.earliestTime(),
#price_currency_value=currency
)
user_trade_condition.validate()
sale_supply = self.portal.sale_supply_module.newContent(
portal_type="Sale Supply",
source_section=internal_trade_condition.getSpecialiseValue().getSourceSection(),
start_date_range_min=now.earliestTime(),
price_currency=internal_trade_condition.getSpecialiseValue().getPriceCurrency()
)
sale_supply.newContent(
portal_type="Sale Supply Line",
base_price=345,
resource="service_module/slapos_virtual_master_subscription"
)
sale_supply.validate()
self.tic()
self.logout()
self.login(customer_person.getUserId())
self.checkVirtualMasterSubscription(customer_person, now)
self.checkSiteConsistency()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSERP5VirtualMasterSubscriptionRequestScenario</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSERP5VirtualMasterSubscriptionRequestScenario</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -23,42 +23,48 @@ from erp5.component.test.SlapOSTestCaseMixin import \
class TestSlapOSSubscriptionRequestProcessAlarm(SlapOSTestCaseMixin):
def test_alarm_slapos_subscription_request_create_from_orphaned_item(self):
def _test_alarm_slapos_subscription_request_create_from_orphaned_item(self, portal_type):
script_name = "Item_createSubscriptionRequest"
alarm = self.portal.portal_alarms.slapos_subscription_request_create_from_orphaned_item
#####################################################
# Instance Tree without Subscription Request
instance_tree = self.portal.instance_tree_module.newContent(
portal_type='Instance Tree',
title="Test Instance Tree no subscription %s" % self.new_id
document = self.portal.getDefaultModule(portal_type).newContent(
portal_type=portal_type,
title="Test %s no subscription %s" % (portal_type, self.new_id)
)
self._test_alarm(alarm, instance_tree, script_name)
self._test_alarm(alarm, document, script_name)
#####################################################
# Instance Tree with Subscription Request
instance_tree = self.portal.instance_tree_module.newContent(
portal_type='Instance Tree',
title="Test Instance Tree no subscription %s" % self.new_id
document = self.portal.getDefaultModule(portal_type).newContent(
portal_type=portal_type,
title="Test %s no subscription %s" % (portal_type, self.new_id)
)
self.portal.subscription_request_module.newContent(
portal_type='Subscription Request',
title="Test Subscription Request %s" % self.new_id,
aggregate_value=instance_tree
aggregate_value=document
)
self._test_alarm_not_visited(alarm, instance_tree, script_name)
self._test_alarm_not_visited(alarm, document, script_name)
#####################################################
# Instance Tree aggregated to another portal type
instance_tree = self.portal.instance_tree_module.newContent(
portal_type='Instance Tree',
title="Test Instance Tree another portal type %s" % self.new_id
document = self.portal.getDefaultModule(portal_type).newContent(
portal_type=portal_type,
title="Test %s another portal type %s" % (portal_type, self.new_id)
)
self.portal.sale_packing_list_module.newContent(
portal_type='Sale Packing List',
title="Test Sale Packing List %s" % self.new_id,
).newContent(
portal_type="Sale Packing List Line",
aggregate_value=instance_tree
aggregate_value=document
)
self._test_alarm(alarm, instance_tree, script_name)
self._test_alarm(alarm, document, script_name)
def test_alarm_slapos_subscription_request_create_from_orphaned_instance_tree(self):
self._test_alarm_slapos_subscription_request_create_from_orphaned_item("Instance Tree")
def test_alarm_slapos_subscription_request_create_from_orphaned_compute_node(self):
self._test_alarm_slapos_subscription_request_create_from_orphaned_item("Compute Node")
......@@ -103,157 +103,6 @@ class TestSubscriptionSkinsMixin(SlapOSTestCaseMixinWithAbort):
self.tic()
return subscription_request
class TestBase_instanceXmlToDict(TestSubscriptionSkinsMixin):
def test_Base_instanceXmlToDict(self):
xml = """<?xml version="1.0" encoding="utf-8"?>
<instance>
</instance>"""
self.assertEqual(self.portal.Base_instanceXmlToDict(xml), {})
def test_Base_instanceXmlToDict_simple_parameter(self):
xml = """<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>"""
self.assertEqual(self.portal.Base_instanceXmlToDict(xml), {'oi': 'couscous', 'zz': 'yy'})
def test_Base_instanceXmlToDict_serialise_parameter(self):
xml = """<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="_">{"aa": "bb"}</parameter>
</instance>"""
self.assertEqual(self.portal.Base_instanceXmlToDict(xml), {'_': '{"aa": "bb"}'})
class TestSubscriptionCondition_renderKVMClusterParameter(TestSubscriptionSkinsMixin):
def test_simple_raise_if_request_is_present(self):
subscription_condition = self.newSubscriptionCondition()
self.assertRaises(Unauthorized, subscription_condition.SubscriptionCondition_renderKVMClusterParameter,
REQUEST=self.portal.REQUEST)
def test_simple_parameter_is_none(self):
subscription_condition = self.newSubscriptionCondition()
self.assertEqual(None, subscription_condition.SubscriptionCondition_renderKVMClusterParameter(amount=0))
self.assertEqual(None, subscription_condition.SubscriptionCondition_renderKVMClusterParameter(amount=1))
def test_simple_raise_if_parameter_isnt_serialised(self):
parameter_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oioi">notserialised</parameter>
</instance>"""
subscription_condition = self.newSubscriptionCondition(
text_content=parameter_xml)
self.assertRaises(ValueError, subscription_condition.SubscriptionCondition_renderKVMClusterParameter,
amount=5)
def test_simple_kvm_rendering(self):
parameter_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="_">{
"kvm-partition-dict": {
"KVM0": {
"cpu-count": 40,
"cpu-max-count": 41,
"ram-size": 245760,
"ram-max-size": 245761,
"disk-device-path": "/dev/sdb",
"project-guid": "PROJ-XXXX",
"disable-ansible-promise": true
}
}
}</parameter>
</instance>"""
subscription_condition = self.newSubscriptionCondition(
text_content=parameter_xml)
# Amount is 0, so return None
self.assertEqual(None, subscription_condition.SubscriptionCondition_renderKVMClusterParameter())
self.assertEqual(None, subscription_condition.SubscriptionCondition_renderKVMClusterParameter(amount=0))
self.assertEqual(subscription_condition.SubscriptionCondition_renderKVMClusterParameter(amount=1).strip(),
"""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="_">{
"kvm-partition-dict": {
"KVM0": {
"disable-ansible-promise": true,
"disk-device-path": "/dev/sdb",
"cpu-count": 40,
"ram-max-size": 245761,
"ram-size": 245760,
"project-guid": "PROJ-XXXX",
"cpu-max-count": 41,
"sticky-compute-node": true
}
}
}</parameter>
</instance>""")
self.assertEqual(subscription_condition.SubscriptionCondition_renderKVMClusterParameter(amount=2).strip(),
"""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="_">{
"kvm-partition-dict": {
"KVM0": {
"disable-ansible-promise": true,
"disk-device-path": "/dev/sdb",
"cpu-count": 40,
"ram-max-size": 245761,
"ram-size": 245760,
"project-guid": "PROJ-XXXX",
"cpu-max-count": 41,
"sticky-compute-node": true
},
"KVM1": {
"disk-device-path": "/dev/sdb",
"cpu-count": 40,
"ram-max-size": 245761,
"cpu-max-count": 41,
"disable-ansible-promise": true,
"ram-size": 245760,
"project-guid": "PROJ-XXXX"
}
}
}</parameter>
</instance>""")
class TestSubscriptionCondition_renderParameter(TestSubscriptionSkinsMixin):
@simulate('SubscriptionCondition_renderParameterSampleScript', '*args, **kwargs','return args, kwargs')
def test_call_script(self):
subscription_condition = self.newSubscriptionCondition()
subscription_condition.setParameterTemplateRendererMethodId("SubscriptionCondition_renderParameterSampleScript")
self.assertEqual(((), {'amount': 1}), subscription_condition.SubscriptionCondition_renderParameter(amount=1))
def test_script_is_not_set(self):
parameter_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="_">{
"kvm-partition-dict": {
"KVM0": {
"cpu-count": 40,
"cpu-max-count": 41,
"ram-size": 245760,
"ram-max-size": 245761,
"disk-device-path": "/dev/sdb",
"project-guid": "PROJ-XXXX",
"disable-ansible-promise": true
}
}
}</parameter>
</instance>"""
subscription_condition = self.newSubscriptionCondition()
self.assertEqual(None, subscription_condition.SubscriptionCondition_renderParameter(amount=1))
subscription_condition.setTextContent(parameter_xml)
self.assertEqual(parameter_xml, subscription_condition.SubscriptionCondition_renderParameter(amount=1))
class TestSubscriptionRequest_init(TestSubscriptionSkinsMixin):
def test_SubscriptionRequest_init(self):
......@@ -672,74 +521,6 @@ class TestSubscriptionRequest_createRelatedSaleInvoiceTransaction(TestSubscripti
def test_creation_of_related_sale_invoice_transaction_q10(self):
self._test_creation_of_related_sale_invoice_transaction(10)
class SubscriptionRequest_processRequest(TestSubscriptionSkinsMixin):
def test_process_request_person_is_none(self):
subscription_request = self.newSubscriptionRequest(quantity=1)
self.assertEqual(None, subscription_request.SubscriptionRequest_processRequest())
def test_process_request_simulation_state(self):
person = self.makePerson()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
text_content="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
subscription_request.plan()
subscription_request.order()
subscription_request.confirm()
self.assertEqual(None, subscription_request.SubscriptionRequest_processRequest())
def test_process_request(self):
person = self.makePerson()
subscription_condition = self.newSubscriptionCondition(
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
text_content="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
specialise_value=subscription_condition
)
self.tic()
subscription_request.SubscriptionRequest_processRequest()
software_instance = subscription_request.getAggregateValue(portal_type="Instance Tree")
self.assertEqual(software_instance.getSourceReference(), "test_for_test_123")
self.assertEqual(software_instance.getUrlString(), "https://%s/software.cfg" % self.new_id)
self.assertEqual(software_instance.getTextContent(), """<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""")
self.assertEqual(software_instance.getSlaXml(), """<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""")
self.assertEqual(software_instance.getSlapState(), "start_requested")
class TestSubscriptionRequest_sendAcceptedNotification(TestSubscriptionSkinsMixin):
......@@ -1091,230 +872,6 @@ class TestSubscriptionRequest_verifyReservationPaymentTransaction(TestSubscripti
def test_deleted_sale_invoice_state(self):
self._test_cancel_due_sale_invoice_state(state="deleted")
class TestSubscriptionRequest_processOrdered(TestSubscriptionSkinsMixin):
def test_no_sale_invoice(self):
person = self.makePerson()
subscription_condition = self.newSubscriptionCondition(
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
text_content="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
specialise_value=subscription_condition
)
self.tic()
self.assertEqual(
subscription_request.SubscriptionRequest_processOrdered(),
None)
subscription_request.plan()
subscription_request.order()
self.tic()
self.assertEqual(
subscription_request.SubscriptionRequest_processOrdered(),
'Skipped (Instance Requested)')
self.tic()
instance_tree = subscription_request.getAggregateValue(portal_type="Instance Tree")
self.assertNotEqual(instance_tree, None)
instance = instance_tree.getSuccessorValue()
self.assertNotEqual(instance, None)
self.assertEqual('diverged', instance_tree.getCausalityState())
instance = instance_tree.getSuccessorValue()
self.assertNotEqual(instance, None)
self.assertEqual(
subscription_request.SubscriptionRequest_processOrdered(),
"Skipped (Payment isn't ready)")
self.tic()
self.assertEqual('solved', instance_tree.getCausalityState())
contract = self.portal.portal_catalog.getResultValue(
portal_type=["Cloud Contract"],
default_destination_section_uid=person.getUid(),
validation_state=['invalidated', 'validated'],
)
self.assertNotEqual(contract, None)
# here the Reservation fee invoice wasn't generated to the user
# don't get cloud contact approved
self.assertEqual(contract.getValidationState(), "invalidated")
# Check if refundable invoice wasn't generated as causality is None anyway...
invoice = subscription_request.getCausalityValue(
portal_type="Sale Invoice Transaction")
self.assertEqual(None, invoice)
self.assertEqual(
subscription_request.getSimulationState(),
"ordered"
)
@simulate('SubscriptionRequest_verifyPaymentBalanceIsReady', '*args, **kwrgs', 'return None')
@simulate('InstanceTree_requestUpdateOpenSaleOrder', '*args, **kwargs', 'context.converge()')
@simulate('SubscriptionRequest_verifyInstanceIsAllocated', '*args, **kwargs','return True')
def test_with_reservation_fee(self):
person = self.makePerson()
subscription_condition = self.newSubscriptionCondition(
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
text_content="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123",
specialise_value=self.newSaleTradeCondition()
)
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
specialise_value=subscription_condition
)
self.tic()
# The SubscriptionRequest_createRelatedSaleInvoiceTransaction is invoked up, as it proven on
# test TestSubscriptionRequest_requestPaymentTransaction, so let's keep it simple, and just reinvoke
current_payment = subscription_request.SubscriptionRequest_requestPaymentTransaction("TAG")
self.assertNotEqual(current_payment, None)
self.tic()
self.assertEqual(
subscription_request.SubscriptionRequest_processOrdered(), None)
subscription_request.plan()
subscription_request.order()
self.tic()
self.assertEqual(
subscription_request.SubscriptionRequest_processOrdered(), 'Skipped (Instance Requested)')
self.tic()
instance_tree = subscription_request.getAggregateValue(portal_type="Instance Tree")
self.assertNotEqual(instance_tree, None)
self.assertEqual('diverged', instance_tree.getCausalityState())
self.assertEqual('start_requested', instance_tree.getSlapState())
instance = instance_tree.getSuccessorValue()
self.assertNotEqual(instance, None)
self.assertEqual(
subscription_request.SubscriptionRequest_processOrdered(),
"Skipped (Payment isn't ready)")
self.tic()
self.assertEqual('solved', instance_tree.getCausalityState())
contract = self.portal.portal_catalog.getResultValue(
portal_type=["Cloud Contract"],
default_destination_section_uid=person.getUid(),
validation_state=['invalidated', 'validated'],
)
self.assertNotEqual(contract, None)
# here the Reservation fee invoice wasn't generated to the user
# don't get cloud contact approved
self.assertEqual(contract.getValidationState(), "invalidated")
# Check if refundable invoice wasn't generated as causality is None anyway...
invoice = subscription_request.getCausalityValue(
portal_type="Sale Invoice Transaction")
self.assertNotEqual(None, invoice)
self.assertEqual(
subscription_request.getSimulationState(),
"ordered"
)
@simulate('SubscriptionRequest_verifyPaymentBalanceIsReady', '*args, **kwrgs', 'return context.fake_payment')
@simulate('InstanceTree_requestUpdateOpenSaleOrder', '*args, **kwargs', 'context.converge()')
@simulate('SubscriptionRequest_verifyInstanceIsAllocated', '*args, **kwargs','return True')
def test_confirmed(self):
person = self.makePerson()
subscription_condition = self.newSubscriptionCondition(
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
text_content="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123",
specialise_value=self.newSaleTradeCondition()
)
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
specialise_value=subscription_condition
)
subscription_request.plan()
subscription_request.order()
self.createNotificationMessage("subscription_request-payment-is-ready",
text_content='${name} ${subscription_title} ${payment_relative_relative_url}')
fake_invoice = self.portal.accounting_module.newContent(
portal_type="Sale Invoice Transaction"
)
fake_payment = self.portal.accounting_module.newContent(
portal_type="Payment Transaction",
causality=fake_invoice.getRelativeUrl())
# Set this to the mock script can return it
setattr(subscription_request, 'fake_payment', fake_payment)
self.tic()
self.assertEqual(
subscription_request.SubscriptionRequest_processOrdered(),
'Skipped (Instance Requested)')
self.tic()
instance_tree = subscription_request.getAggregateValue()
self.assertNotEqual(None, instance_tree)
self.assertEqual('diverged', instance_tree.getCausalityState())
self.assertEqual(
subscription_request.SubscriptionRequest_processOrdered(),
'Payment is ready for the user')
self.tic()
self.assertEqual('solved', instance_tree.getCausalityState())
self.assertEqual(
subscription_request.getSimulationState(),
"confirmed"
)
class TestSubscriptionRequest_verifyInstanceIsAllocated(TestSubscriptionSkinsMixin):
......@@ -1407,253 +964,6 @@ class TestSubscriptionRequest_verifyInstanceIsAllocated(TestSubscriptionSkinsMix
self.assertEqual(
subscription_request.SubscriptionRequest_verifyInstanceIsAllocated(), True)
class TestSubscriptionRequest_processConfirmed(TestSubscriptionSkinsMixin):
def test_no_instance_tree(self):
person = self.makePerson()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
text_content="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
subscription_request.plan()
subscription_request.order()
subscription_request.confirm()
self.tic()
self.assertEqual(
subscription_request.SubscriptionRequest_processConfirmed(), None)
self.tic()
self.assertEqual(subscription_request.getSimulationState(), "confirmed")
@simulate('SubscriptionRequest_testPaymentBalance', '', 'return False')
def test_instance_tree_is_stopped_due_unpaid_invoice(self):
person = self.makePerson()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
text_content="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
subscription_request.plan()
subscription_request.order()
subscription_request.confirm()
self._makeTree()
subscription_request.edit(
aggregate_value=self.instance_tree
)
self.tic()
self.assertEqual(
subscription_request.SubscriptionRequest_processConfirmed(),
'Skipped (Payment is pending)')
self.tic()
self.assertEqual(subscription_request.getSimulationState(), "confirmed")
self.assertEqual(self.instance_tree.getSlapState(), "stop_requested")
@simulate('SubscriptionRequest_testPaymentBalance', '', 'return True')
def test_instance_tree_is_started_due_paid_invoice(self):
person = self.makePerson()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
text_content="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
subscription_request.plan()
subscription_request.order()
subscription_request.confirm()
self._makeTree()
subscription_request.edit(
aggregate_value=self.instance_tree
)
self.portal.portal_workflow._jumpToStateFor(self.instance_tree, 'stop_requested')
self.tic()
self.assertEqual(
subscription_request.SubscriptionRequest_processConfirmed(),
'Skipped (Started instance)')
self.tic()
self.assertEqual(subscription_request.getSimulationState(), "confirmed")
self.assertEqual(self.instance_tree.getSlapState(), "start_requested")
class TestSubscriptionRequest_processStarted(TestSubscriptionSkinsMixin):
def test_no_instance_tree(self):
person = self.makePerson()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
text_content="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
subscription_request.plan()
subscription_request.order()
subscription_request.confirm()
subscription_request.start()
self.tic()
self.assertEqual(
subscription_request.SubscriptionRequest_processStarted(), None)
self.tic()
self.assertEqual(subscription_request.getSimulationState(), "started")
def test_instance_tree_started(self):
person = self.makePerson()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
text_content="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
subscription_request.plan()
subscription_request.order()
subscription_request.confirm()
subscription_request.start()
self._makeTree()
subscription_request.edit(
aggregate_value=self.instance_tree
)
self.tic()
self.assertEqual(
subscription_request.SubscriptionRequest_processStarted(), None)
self.tic()
self.assertEqual(subscription_request.getSimulationState(), "started")
def test_instance_tree_stopped(self):
person = self.makePerson()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
text_content="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
subscription_request.plan()
subscription_request.order()
subscription_request.confirm()
subscription_request.start()
self._makeTree()
subscription_request.edit(
aggregate_value=self.instance_tree
)
self.portal.portal_workflow._jumpToStateFor(self.instance_tree, 'stop_requested')
self.tic()
self.assertEqual(
subscription_request.SubscriptionRequest_processStarted(), None)
self.tic()
self.assertEqual(subscription_request.getSimulationState(), "started")
def test_instance_tree_destroyed(self):
person = self.makePerson()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
text_content="""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
subscription_request.plan()
subscription_request.order()
subscription_request.confirm()
subscription_request.start()
self._makeTree()
subscription_request.edit(
aggregate_value=self.instance_tree
)
self.portal.portal_workflow._jumpToStateFor(self.instance_tree, 'destroy_requested')
self.tic()
self.assertEqual(
subscription_request.SubscriptionRequest_processStarted(), None)
self.tic()
self.assertEqual(subscription_request.getSimulationState(), "stopped")
class TestSlapOSSubscriptionRequestModule_getTicketFeedUrl(TestSubscriptionSkinsMixin):
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Interaction Workflow Interaction" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>after_script/portal_workflow/slapos_subscription_request_interaction_workflow/script_Base_triggerValidationAlarm</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>interaction_SaleInvoiceTransactionLine_setGroupingReference</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Interaction Workflow Interaction</string> </value>
</item>
<item>
<key> <string>portal_type_filter</string> </key>
<value>
<tuple>
<string>Sale Invoice Transaction Line</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type_group_filter</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>trigger_method_id</string> </key>
<value>
<tuple>
<string>_setGroupingReference</string>
<string>_baseSetGroupingReference</string>
</tuple>
</value>
</item>
<item>
<key> <string>trigger_once_per_transaction</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
if alarm.getParentValue().isSubscribed() and not alarm.isActive() and alarm.isEnabled():
alarm.activate(queue='SQLQueue', **activate_kw).activeSense()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Script" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>alarm, activate_kw</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_Alarm_safeTrigger</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Script</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
base = state_change['object']
portal = base.getPortalObject()
tag = script.id
base.reindexObject(activate_kw={'tag': tag})
context.Alarm_safeTrigger(portal.portal_alarms.slapos_subscription_request_create_from_orphaned_item, {'after_tag': tag})
return state_change['object'].Base_reindexAndSenseAlarm(['slapos_subscription_request_create_from_orphaned_item'])
base = state_change['object']
portal = base.getPortalObject()
tag = script.id
base.reindexObject(activate_kw={'tag': tag})
context.Alarm_safeTrigger(portal.portal_alarms.slapos_subscription_request_validate_submitted, {'after_tag': tag})
return state_change['object'].Base_reindexAndSenseAlarm(['slapos_subscription_request_validate_submitted'])
Compute Node | slapos_subscription_request_interaction_workflow
Email | -coordinate_interaction_workflow
Instance Tree | slapos_subscription_request_interaction_workflow
Sale Invoice Transaction Line | slapos_subscription_request_interaction_workflow
Subscription Condition | commerce_validation_workflow
Subscription Condition | edit_workflow
Subscription Request | edit_workflow
......
test.erp5.testSlapOSERP5VirtualMasterSubscriptionRequestScenario
test.erp5.testSlapOSSubscriptionAlarm
test.erp5.testSlapOSSubscriptionSkins
\ 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