Commit a226ded9 authored by Romain Courteaud's avatar Romain Courteaud

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX EXPAND

slapos_accounting:

* no need to create open order slowly
* trade condition has more than one supply line
* WIP create open order for compute node
* alarm to create compute node open order
* categrory
* define comp node subscription price

slapos_cloud:

* approve computer again

slapos_subscription_request:

* trigger compute node open order creation
parent 9ff4cef3
<?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_requestUpdateComputeNodeOpenSaleOrder</string> </value>
</item>
<item>
<key> <string>automatic_solve</string> </key>
<value> <int>0</int> </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_request_update_compute_node_open_sale_order</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>periodicity_day_frequency</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>periodicity_hour</string> </key>
<value>
<tuple>
<int>0</int>
</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>
<int>31</int>
</tuple>
</value>
</item>
<item>
<key> <string>periodicity_minute_frequency</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>periodicity_month</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_month_day</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>periodicity_start_date</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>0.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>title</string> </key>
<value> <string>Updates Open Sale Order for Compute Node</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -87,10 +87,10 @@
<value>
<tuple>
<string>specialise/sale_trade_condition_module/slapos_subscription_trade_condition</string>
<string>source_section/organisation_module/slapos</string>
<string>destination_section/organisation_module/slapos</string>
<string>source/organisation_module/slapos</string>
<string>price_currency/currency_module/EUR</string>
<string>source/organisation_module/slapos</string>
<string>source_section/organisation_module/slapos</string>
</tuple>
</value>
</item>
......@@ -240,7 +240,7 @@
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
<value> <string>ERP5TypeTestCase</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
......@@ -254,7 +254,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>999.4006.48979.14984</string> </value>
<value> <string>999.14293.29385.24115</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -272,7 +272,7 @@
</tuple>
<state>
<tuple>
<float>1648026796.66</float>
<float>1648643921.3</float>
<string>UTC</string>
</tuple>
</state>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Sale Supply Line" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>base_price</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>base_price_per_slice</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>resource/service_module/slapos_compute_node_subscription</string>
</tuple>
</value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>compute_node_subscription_price_eur</string> </value>
</item>
<item>
<key> <string>index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>mapped_value_property_list</string> </key>
<value>
<tuple>
<string>base_price</string>
<string>additional_price</string>
<string>discount_ratio</string>
<string>exclusive_discount_ratio</string>
<string>surcharge_ratio</string>
<string>variable_additional_price</string>
<string>non_discountable_additional_price</string>
<string>priced_quantity</string>
<string>base_unit_price</string>
<string>quantity_unit</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Sale Supply Line</string> </value>
</item>
<item>
<key> <string>price</string> </key>
<value> <float>0.0</float> </value>
</item>
<item>
<key> <string>priced_quantity</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Compute Node Subscription price</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>path</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <int>0</int> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAY=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="6" aka="AAAAAAAAAAY=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -77,6 +77,10 @@
<key> <string>portal_type</string> </key>
<value> <string>Sale Supply Line</string> </value>
</item>
<item>
<key> <string>price</string> </key>
<value> <float>0.0</float> </value>
</item>
<item>
<key> <string>priced_quantity</string> </key>
<value> <float>1.0</float> </value>
......
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
method_id='ComputeNode_requestUpdateOpenSaleOrder',
method_kw=dict(specialise="sale_trade_condition_module/default_subscription_trade_condition", ),
portal_type="Compute Node",
activate_kw={'tag': tag, 'priority': 2},
activity_count=10,
)
context.activate(after_tag=tag).getId()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>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>_params</string> </key>
<value> <string>tag, fixit, params</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Alarm_requestUpdateComputeNodeOpenSaleOrder</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -7,7 +7,6 @@ portal.portal_catalog.searchAndActivate(
causality_state="diverged",
activate_kw={'tag': tag, 'priority': 2},
activity_count=10,
packet_size=1, # InstanceTree_trigger_Person_storeOpenSaleOrderJournal
)
context.activate(after_tag=tag).getId()
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
from DateTime import DateTime
portal = context.getPortalObject()
compute_node = context
tag = '%s_%s' % (compute_node.getUid(), script.id)
activate_kw = {'tag': tag}
if portal.portal_activities.countMessageWithTag(tag) > 0:
# nothing to do
return
def storeWorkflowComment(document, comment):
portal.portal_workflow.doActionFor(document, 'edit_action', comment=comment)
def newOpenOrder():
new_open_sale_order = portal.open_sale_order_module.newContent(portal_type="Open Sale Order")
new_open_sale_order.edit(
specialise=specialise,
effective_date=DateTime(),
activate_kw=activate_kw,
destination=person.getRelativeUrl(),
destination_decision=person.getRelativeUrl(),
title="%s SlapOS Subscription" % person.getTitle()
)
new_open_sale_order.order(activate_kw=activate_kw)
new_open_sale_order.validate(activate_kw=activate_kw)
return new_open_sale_order
if 1:
open_order = None
person = compute_node.getSourceAdministrationValue(portal_type="Person")
# Template document does not have person relation
if person is not None:
# Search an existing related open order
open_order_line = portal.portal_catalog.getResultValue(
portal_type='Open Sale Order Line',
default_aggregate_uid=compute_node.getUid())
is_open_order_creation_needed = False
# Simply check that it has never been simulated
if 0:#instance_tree.getSlapState() == 'destroy_requested':
# Line should be deleted
if (open_order_line is not None) and (open_order_line.getValidationState() == "invalidated"):
instance_tree.converge(comment="Last open order: %s" % open_order_line.getRelativeUrl())
elif open_order_line is None:
# User has no Open Sale Order (likely).
# No need to charge, as it was never allocated
is_open_order_creation_needed = False
instance_tree.converge(comment="No open order needed as it was never allocated")
elif open_order_line is None:
# Let's add
is_open_order_creation_needed = True
# Let's create the open order
if is_open_order_creation_needed:
open_sale_order = newOpenOrder()
open_order_explanation = ""
# Add lines
open_order_line = open_sale_order.newContent(portal_type="Open Sale Order Line")
hosting_subscription = portal.hosting_subscription_module.newContent(
portal_type="Hosting Subscription",
title=compute_node.getTitle()
)
hosting_subscription.validate()
start_date = hosting_subscription.HostingSubscription_calculateSubscriptionStartDate()
# Search for matching resource
service_list = portal.portal_catalog(
# XXX Hardcoded as temporary
id='slapos_compute_node_subscription',
portal_type='Service',
validation_state='validated',
use__relative_url='use/trade/sale'
)
service = [x for x in service_list if compute_node.getPortalType() in x.getRequiredAggregatedPortalTypeList()][0].getObject()
edit_kw = {
'quantity': 1,
'resource_value': service,
'quantity_unit': service.getQuantityUnit(),
'base_contribution_list': service.getBaseContributionList(),
'use': service.getUse()
}
open_order_line.edit(
activate_kw=activate_kw,
title=compute_node.getTitle(),
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,
# stop_date=calculateOpenOrderLineStopDate(open_sale_order_line,
# instance_tree, start_date_delta=start_date_delta),
aggregate_value_list=[hosting_subscription, compute_node],
**edit_kw
)
predicate_list = []
inherited_trade_condition = open_sale_order.getSpecialiseValue()
while inherited_trade_condition is not None:
predicate_list.extend([
x for x in inherited_trade_condition.contentValues(portal_type='Sale Supply Line')
if x.getResource() == service.getRelativeUrl()
])
inherited_trade_condition = inherited_trade_condition.getSpecialiseValue(portal_type=inherited_trade_condition.getPortalType())
price = service.getPrice(
context=open_order_line,
predicate_list=predicate_list,
default=None,
)
if price is None:
raise NotImplementedError('Price must be defined')
open_order_line.edit(
price=price
)
storeWorkflowComment(open_order_line, "Created for %s" % compute_node.getRelativeUrl())
# instance_tree.converge(comment="Last open order: %s" % open_sale_order_line.getRelativeUrl())
open_order_explanation = "Added %s." % str(open_order_line.getId())
storeWorkflowComment(open_sale_order, open_order_explanation)
open_order = open_order_line.getParentValue()
open_order.SaleOrder_applySaleTradeCondition(batch_mode=1, force=1)
# Check compatibility with previous template
assert open_order.getSpecialise() == specialise
if open_order_line is not None:
open_order = open_order_line.getParentValue()
assert open_order_line.getResource() == 'service_module/slapos_compute_node_subscription'
assert open_order_line.getQuantityUnit() == 'unit/piece'
assert open_order_line.getBaseContribution() == 'base_amount/invoicing/discounted'
assert open_order_line.getBaseContributionList()[1] == 'base_amount/invoicing/taxable'
assert open_order_line.getUse() == 'trade/sale'
#assert open_order_line.getPrice() == 1, open_order_line.getPrice()
#assert open_order_line.getQuantity() == 1
# XXX TODO open_order.OpenSaleOrder_updatePeriod()
# Person_storeOpenSaleOrderJournal should fix all divergent Instance Tree in one run
# assert instance_tree.getCausalityState() == 'solved'
return open_order
<?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>_params</string> </key>
<value> <string>specialise, REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ComputeNode_requestUpdateOpenSaleOrder</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -193,7 +193,9 @@ class TestInstanceTree_requestUpdateOpenSaleOrder(SlapOSTestCaseMixin):
"sale_trade_condition_module/default_subscription_trade_condition",
sale_trade_condition.getRelativeUrl()
)
sale_supply_line = sale_trade_condition.contentValues(portql_type="Sale Supply Line")[0]
sale_supply_line = sale_trade_condition.searchFolder(
portal_type="Sale Supply Line",
resource__relative_url='service_module/slapos_instance_subscription')[0]
open_sale_order_line_list = open_sale_order.contentValues(
portal_type='Open Sale Order Line')
......@@ -274,7 +276,9 @@ class TestInstanceTree_requestUpdateOpenSaleOrder(SlapOSTestCaseMixin):
"sale_trade_condition_module/default_subscription_trade_condition",
sale_trade_condition.getRelativeUrl()
)
sale_supply_line = sale_trade_condition.contentValues(portql_type="Sale Supply Line")[0]
sale_supply_line = sale_trade_condition.searchFolder(
portal_type="Sale Supply Line",
resource__relative_url='service_module/slapos_instance_subscription')[0]
self.assertEqual(1, len(open_sale_order_line_list))
line = open_sale_order_line_list[0].getObject()
......@@ -455,7 +459,9 @@ class TestInstanceTree_requestUpdateOpenSaleOrder(SlapOSTestCaseMixin):
"sale_trade_condition_module/default_subscription_trade_condition",
sale_trade_condition.getRelativeUrl()
)
sale_supply_line = sale_trade_condition.contentValues(portql_type="Sale Supply Line")[0]
sale_supply_line = sale_trade_condition.searchFolder(
portal_type="Sale Supply Line",
resource__relative_url='service_module/slapos_instance_subscription')[0]
open_sale_order_line_list = open_sale_order.contentValues(
portal_type='Open Sale Order Line')
......
......@@ -29,4 +29,5 @@ sale_trade_condition_module/slapos_subscription_trade_condition/**
sale_trade_condition_module/slapos_legacy_aggregated_subscription_trade_condition
sale_trade_condition_module/slapos_legacy_aggregated_subscription_trade_condition/**
sale_trade_condition_module/slapos_legacy_aggregated_subscription_trade_condition_2022
sale_trade_condition_module/slapos_legacy_aggregated_subscription_trade_condition_2022/**
\ No newline at end of file
sale_trade_condition_module/slapos_legacy_aggregated_subscription_trade_condition_2022/**
service_module/slapos_compute_node_subscription
......@@ -14,4 +14,5 @@ service_module/slapos_reservation_fee/**
service_module/slapos_reservation_refund
service_module/cpu_load_percent
service_module/memory_used
service_module/disk_used
\ No newline at end of file
service_module/disk_used
service_module/slapos_compute_node_subscription
\ No newline at end of file
......@@ -31,6 +31,7 @@ portal_alarms/slapos_deliver_started_aggregated_sale_packing_list
portal_alarms/slapos_manage_building_calculating_delivery
portal_alarms/slapos_reindex_open_sale_order
portal_alarms/slapos_remove_bogus_delivery_link
portal_alarms/slapos_request_update_compute_node_open_sale_order
portal_alarms/slapos_request_update_instance_tree_open_sale_order
portal_alarms/slapos_start_confirmed_aggregated_sale_packing_list
portal_alarms/slapos_start_confirmed_aggregated_subscription_sale_packing_list
......@@ -99,6 +100,7 @@ service_module/cpu_load_percent
service_module/disk_used
service_module/memory_used
service_module/slapos_account_validation
service_module/slapos_compute_node_subscription
service_module/slapos_discount
service_module/slapos_instance_cleanup
service_module/slapos_instance_hosting
......
......@@ -36,6 +36,7 @@ else:
reference=reference,
activate_kw={'tag': tag}
)
compute_node.approveComputeNodeRegistration()
compute_node = context.restrictedTraverse(compute_node.getRelativeUrl())
......
......@@ -1680,6 +1680,11 @@ return dict(vads_url_already_registered="%s/already_registered" % (payment_trans
self.tic()
self.subscription_server = self.createPublicServerForAdminUser()
self.tic()
# Trigger open order creation
self.portal.portal_alarms.slapos_request_update_compute_node_open_sale_order.activeSense()
self.tic()
# Disable on this test the pricing on the template to not generate debt before
# them expected
......
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