Commit a2058567 authored by Romain Courteaud's avatar Romain Courteaud

slapos_*: define subscription prices on Sale Trade Condition

Prices are defined on Supply Line inside Sale Trade Condition.

Drop prices from Subscription Condition.
Instead, link the Subscription Condition to a Sale Trade Condition.

Apply the Sale Trade Condition on the created Open Sale Order.

Stop duplicating properties from the Subscription Condition to the created Subscription Request.

Payment is managed by the 'payment' category, and not the language.

This allows to remove all '==zh' conditions.
Instead, use custom Sale Trade Condition.
parent f207a60d
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Open Sale Order 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>categories</string> </key>
<value>
<tuple>
<string>resource/service_module/slapos_instance_subscription</string>
<string>quantity_unit/unit/piece</string>
<string>base_contribution/base_amount/invoicing/discounted</string>
<string>base_contribution/base_amount/invoicing/taxable</string>
<string>use/trade/sale</string>
</tuple>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Price is an TCC (20% included)</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_accounting_open_sale_order_line_template</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>mapped_value_property_list</string> </key>
<value>
<tuple>
<string>priced_quantity</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Open Sale Order Line</string> </value>
</item>
<item>
<key> <string>price</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>quantity</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>SlapOS Accounting Open Sale Order Line Template</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -2,145 +2,100 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Open Sale Order" module="erp5.portal_type"/>
<global name="Sale Supply Line" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<key> <string>_identity_criterion</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<key> <string>_range_criterion</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_Delete_objects_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
<key> <string>base_price</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
<key> <string>base_price_per_slice</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Author</string>
<string>Manager</string>
<string>Owner</string>
<string>resource/service_module/slapos_instance_subscription</string>
</tuple>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
<key> <string>comment</string> </key>
<value> <string>Price is an TCC (20% included)</string> </value>
</item>
<item>
<key> <string>_tree</string> </key>
<key> <string>description</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAY=</string> </persistent>
<none/>
</value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
<key> <string>id</string> </key>
<value> <string>subscription_price</string> </value>
</item>
<item>
<key> <string>default_reference</string> </key>
<key> <string>index</string> </key>
<value>
<none/>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<key> <string>mapped_value_property_list</string> </key>
<value>
<none/>
<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>id</string> </key>
<value> <string>slapos_accounting_open_sale_order_line_template</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Open Sale Order</string> </value>
<value> <string>Sale Supply Line</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>SlapOS Accounting Open Sale Order Line Template</string> </value>
<value> <string>Instance Subscription price</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
......@@ -159,10 +114,24 @@
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<none/>
<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=">
......@@ -174,7 +143,14 @@
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
<dictionary>
<item>
<key> <int>0</int> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAY=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
......@@ -182,10 +158,17 @@
</record>
<record id="6" aka="AAAAAAAAAAY=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<none/>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/string</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_open_sale_order_line_template_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: \'\'</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/string</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>preferred_open_sale_order_template_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>preference</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: \'\'</string> </value>
</item>
<item>
<key> <string>write_permission</string> </key>
<value> <string>Manage properties</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -9,8 +9,8 @@ root_trade_condition_uid_list = [
trade_condition_uid_list.extend(root_trade_condition_uid_list)
trade_condition_uid_list.extend([
i.uid for i in portal.portal_catalog(
specialise__uid=root_trade_condition_uid_list,
i.uid for i in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
specialise_uid=root_trade_condition_uid_list,
validation_state="validated")])
portal.portal_catalog.searchAndActivate(
......
......@@ -2,6 +2,7 @@ portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
method_id='InstanceTree_requestUpdateOpenSaleOrder',
method_kw=dict(specialise="sale_trade_condition_module/couscous_trade_condition", ),
portal_type="Instance Tree",
causality_state="diverged",
activate_kw={'tag': tag, 'priority': 2},
......
......@@ -16,11 +16,18 @@ def getAccountingDate(accounting_date):
accounting_date = params.get('accounting_date', DateTime().earliestTime())
portal = context.getPortalObject()
trade_condition_uid_list = [portal.restrictedTraverse(portal.portal_preferences.getPreferredAggregatedSaleTradeCondition()).getUid()]
trade_condition_uid_list.extend([
i.uid for i in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
specialise_uid=trade_condition_uid_list,
validation_state="validated")])
portal.portal_catalog.searchAndActivate(
portal_type='Sale Packing List',
simulation_state='confirmed',
causality_state='solved',
specialise_uid=portal.restrictedTraverse(portal.portal_preferences.getPreferredAggregatedSaleTradeCondition()).getUid(),
specialise_uid=trade_condition_uid_list,
method_id='Delivery_startConfirmedAggregatedSalePackingList',
activate_kw={'tag': tag},
**{'delivery.start_date': Query(range="max",
......
......@@ -12,9 +12,9 @@ root_trade_condition_uid = root_trade_condition_value.getUid()
trade_condition_uid_list.append(root_trade_condition_uid)
trade_condition_uid_list.extend([
i.uid for i in portal.portal_catalog(
i.uid for i in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
portal_type=root_trade_condition_value.getPortalType(),
specialise__uid=root_trade_condition_uid,
specialise_uid=root_trade_condition_uid,
validation_state="validated")])
portal.portal_catalog.searchAndActivate(
......
......@@ -12,9 +12,9 @@ root_trade_condition_uid_list = [
trade_condition_uid_list.extend(root_trade_condition_uid_list)
trade_condition_uid_list.extend([
i.uid for i in portal.portal_catalog(
i.uid for i in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
portal_type="Sale Trade Condition",
specialise__uid=root_trade_condition_uid_list,
specialise_uid=root_trade_condition_uid_list,
validation_state="validated")])
portal.portal_catalog.searchAndActivate(
......
......@@ -13,7 +13,7 @@ specialise_uid = [
]
return context.getSpecialiseUid() in [
i.uid for i in portal.portal_catalog(
i.uid for i in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
portal_type="Sale Trade Condition",
specialise__uid=specialise_uid,
specialise_uid=specialise_uid,
validation_state="validated")]
......@@ -21,8 +21,8 @@ if context.getSimulationState() == 'started' \
trade_condition_uid_list.extend(root_trade_condition_uid_list)
trade_condition_uid_list.extend([
i.uid for i in portal.portal_catalog(
specialise__uid=root_trade_condition_uid_list,
i.uid for i in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
specialise_uid=root_trade_condition_uid_list,
validation_state="validated")])
if context.getSpecialiseUid() not in trade_condition_uid_list:
......
......@@ -24,8 +24,8 @@ if context.getSimulationState() == 'confirmed' \
trade_condition_uid_list.extend(root_trade_condition_uid_list)
trade_condition_uid_list.extend([
i.uid for i in portal.portal_catalog(
specialise__uid=root_trade_condition_uid_list,
i.uid for i in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
specialise_uid=root_trade_condition_uid_list,
validation_state="validated")])
if context.getSpecialiseUid() not in trade_condition_uid_list:
......
......@@ -21,9 +21,9 @@ if context.getSimulationState() == 'confirmed'\
trade_condition_uid_list.extend(root_trade_condition_uid_list)
trade_condition_uid_list.extend([
i.uid for i in portal.portal_catalog(
i.uid for i in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
portal_type="Sale Trade Condition",
specialise__uid=root_trade_condition_uid_list,
specialise_uid=root_trade_condition_uid_list,
validation_state="validated")])
if context.getSpecialiseUid() not in trade_condition_uid_list:
......
portal = context
result_list = []
if specialise_uid is None:
current_uid_list = []
elif same_type(specialise_uid, []) or same_type(specialise_uid, ()):
current_uid_list = list(specialise_uid)
else:
current_uid_list = [specialise_uid]
search_kw = {}
if portal_type is not None:
search_kw['portal_type'] = portal_type
if validation_state is not None:
search_kw['validation_state'] = validation_state
if destination_section__uid is not None:
search_kw['destination_section__uid'] = destination_section__uid
# This is REALLY INEFFICIENT.
# Keep it simple for now, as the goal is probably to drop all this script usage
while (current_uid_list):
specialise__uid = current_uid_list
current_uid_list = []
for sql_result in portal.portal_catalog(
specialise__uid=specialise__uid,
**search_kw
):
current_uid_list.append(sql_result.uid)
result_list.append(sql_result)
return result_list
......@@ -50,11 +50,11 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>root_trade_condition</string> </value>
<value> <string>specialise_uid, portal_type=None, validation_state=None, destination_section__uid=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_getAggregatedSubscriptionSaleTradeConditionValue</string> </value>
<value> <string>ERP5Site_searchRelatedInheritedSpecialiseList</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -7,7 +7,6 @@ from DateTime import DateTime
portal = context.getPortalObject()
instance_tree = context
now = DateTime()
tag = '%s_%s' % (instance_tree.getUid(), script.id)
activate_kw = {'tag': tag}
if portal.portal_activities.countMessageWithTag(tag) > 0:
......@@ -19,32 +18,23 @@ def storeWorkflowComment(document, comment):
portal.portal_workflow.doActionFor(document, 'edit_action', comment=comment)
def newOpenOrder(open_sale_order):
open_sale_order_template = portal.restrictedTraverse(
portal.portal_preferences.getPreferredOpenSaleOrderTemplate())
open_order_edit_kw = {
'effective_date': DateTime(),
'activate_kw': activate_kw,
'source': open_sale_order_template.getSource(),
'source_section': open_sale_order_template.getSourceSection()
}
if open_sale_order is None:
new_open_sale_order = open_sale_order_template.Base_createCloneDocument(batch_mode=1)
open_order_edit_kw.update({
'destination': person.getRelativeUrl(),
'destination_decision': person.getRelativeUrl(),
'title': "%s SlapOS Subscription" % person.getTitle(),
})
else:
new_open_sale_order = open_sale_order.Base_createCloneDocument(batch_mode=1)
open_sale_order.setExpirationDate(now, activate_kw=activate_kw)
new_open_sale_order.edit(**open_order_edit_kw)
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 instance_tree.getCausalityState() == 'diverged':
open_order = None
person = instance_tree.getDestinationSectionValue(portal_type="Person")
# Template document does not have person relation
if person is not None:
......@@ -73,14 +63,12 @@ if instance_tree.getCausalityState() == 'diverged':
# Let's create the open order
if is_open_order_creation_needed:
open_sale_order = newOpenOrder(None)
open_sale_order = newOpenOrder()
open_order_explanation = ""
# Add lines
open_sale_order_line_template = portal.restrictedTraverse(
portal.portal_preferences.getPreferredOpenSaleOrderLineTemplate())
open_order_line = open_sale_order_line_template.Base_createCloneDocument(batch_mode=1,
destination=open_sale_order)
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=instance_tree.getTitle()
......@@ -88,22 +76,39 @@ if instance_tree.getCausalityState() == 'diverged':
hosting_subscription.validate()
start_date = hosting_subscription.HostingSubscription_calculateSubscriptionStartDate()
edit_kw = {}
# Search for matching resource
service_list = portal.portal_catalog(
# XXX Hardcoded as temporary
id='slapos_instance_subscription',
portal_type='Service',
validation_state='validated',
use__relative_url='use/trade/sale'
)
service = [x for x in service_list if instance_tree.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()
}
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:
"""
# Copy from Subscription Condition the source and Source Section into the line
# RAFAEL: As the model is use single Open Order, it isn't possible to use multiple
# companies per region, so we rely on Subscription Conditions to Describe the
# providers.
edit_kw["source"] = subscription_request.getSource()
edit_kw["source_section"] = subscription_request.getSourceSection()
"""
# Quantity is double because the first invoice has to
# charge for 2 months
edit_kw['quantity'] = subscription_request.getQuantity()
"""
edit_kw['price'] = subscription_request.getPrice()
edit_kw['price_currency'] = subscription_request.getPriceCurrency()
# While create move the start date to be at least 1 months
......@@ -112,6 +117,7 @@ if instance_tree.getCausalityState() == 'diverged':
# You can increase 32 days to generate 2 months
# You can increase 0 days to keep generating one month only
# start_date_delta = 0
"""
open_order_line.edit(
activate_kw=activate_kw,
......@@ -125,14 +131,52 @@ if instance_tree.getCausalityState() == 'diverged':
aggregate_value_list=[hosting_subscription, instance_tree],
**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" % instance_tree.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_line.getParentValue().OpenSaleOrder_updatePeriod()
open_order = open_order_line.getParentValue()
assert open_order_line.getResource() == 'service_module/slapos_instance_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
open_order.OpenSaleOrder_updatePeriod()
# Person_storeOpenSaleOrderJournal should fix all divergent Instance Tree in one run
assert instance_tree.getCausalityState() == 'solved'
return open_order
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>REQUEST=None</string> </value>
<value> <string>specialise, REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -10,10 +10,10 @@ business_process_uid_list = [
portal.business_process_module.slapos_reservation_refound_business_process.getUid(),
portal.business_process_module.slapos_subscription_business_process.getUid()]
specialise_uid_list = [q.getUid() for q in portal.portal_catalog(
specialise_uid_list = [q.getUid() for q in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
specialise_uid=business_process_uid_list, portal_type='Sale Trade Condition')]
consumption_specialise_uid_list = [q.getUid() for q in portal.portal_catalog(
consumption_specialise_uid_list = [q.getUid() for q in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
specialise_uid=portal.business_process_module.slapos_consumption_business_process.getUid(),
portal_type='Sale Trade Condition')]
......@@ -74,6 +74,7 @@ for movement in movement_list:
start_date=movement.getStartDate())
# XXX Shamefully hardcoded values
# XXX TODO Drop hardcoded values
if movement.getResource() == 'service_module/slapos_instance_subscription':
if movement.getPriceCurrency() == "currency_module/CNY":
# reduce tax from there directly
......@@ -91,8 +92,7 @@ for movement in movement_list:
if movement.getSpecialiseUid() in consumption_specialise_uid_list:
specialise_to_set = consumption_specialise
else:
person = movement.getDestinationValue()
specialise_to_set = person.Person_getAggregatedSubscriptionSaleTradeConditionValue(subscription_request_specialise)
specialise_to_set = subscription_request_specialise
if instance_tree is not None:
subscription = instance_tree.getAggregateRelated(portal_type="Subscription Request")
......
......@@ -19,7 +19,7 @@ def newPackingList(movement, causality, message):
delivery.confirm(message)
return delivery
consumption_specialise_uid_list = [q.getUid() for q in portal.portal_catalog(
consumption_specialise_uid_list = [q.getUid() for q in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
specialise_uid=portal.business_process_module.slapos_consumption_business_process.getUid(),
portal_type='Sale Trade Condition')]
......
# Search by a trade condition that specialise to the root_trade_condition
# Which is user's specific.
# XXX This code is draft
portal = context.getPortalObject()
if root_trade_condition is None:
return root_trade_condition
root_trade_condition_value = portal.restrictedTraverse(root_trade_condition)
trade_condition = portal.portal_catalog.getResultValue(
portal_type=root_trade_condition_value.getPortalType(),
specialise__uid=root_trade_condition_value.getUid(),
validation_state=root_trade_condition_value.getValidationState(),
destination_section__uid=context.getUid()
)
if trade_condition is not None:
return trade_condition.getRelativeUrl()
return root_trade_condition
......@@ -4,7 +4,7 @@ portal = context.getPortalObject()
business_process_uid_list = [
portal.business_process_module.slapos_subscription_business_process.getUid()]
subscription_delivery_specialise_uid_list = [q.getUid() for q in portal.portal_catalog(
subscription_delivery_specialise_uid_list = [q.getUid() for q in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
specialise_uid=business_process_uid_list, portal_type='Sale Trade Condition')]
search_kw = {
......
......@@ -5,27 +5,26 @@ restrictedTraverse = portal.restrictedTraverse
person = context.getDestination()
reference = context.getReference()
business_process_uid_list = [
portal.business_process_module.slapos_reservation_refound_business_process.getUid(),
portal.business_process_module.slapos_subscription_business_process.getUid()]
business_process_list = [
portal.business_process_module.slapos_reservation_refound_business_process,
portal.business_process_module.slapos_subscription_business_process]
business_process_uid_list = [x.getUid() for x in business_process_list]
specialise_list = [q.getRelativeUrl() for q in portal.portal_catalog(
specialise_list = [q.getRelativeUrl() for q in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
specialise_uid=business_process_uid_list, portal_type='Sale Trade Condition')]
consumption_specialise_list = [q.getRelativeUrl() for q in portal.portal_catalog(
consumption_specialise_list = [q.getRelativeUrl() for q in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
specialise_uid=portal.business_process_module.slapos_consumption_business_process.getUid(),
portal_type='Sale Trade Condition')]
subscription_request_specialise = portal.portal_preferences.getPreferredAggregatedSubscriptionSaleTradeCondition()
consumption_specialise = portal.portal_preferences.getPreferredAggregatedConsumptionSaleTradeCondition()
trade_condition = context.getSpecialise()
specialise_filter_list = consumption_specialise_list + specialise_list
if trade_condition == consumption_specialise:
if trade_condition in consumption_specialise_list:
specialise_filter_list = consumption_specialise_list
elif trade_condition == subscription_request_specialise:
elif trade_condition in specialise_list:
specialise_filter_list = specialise_list
else:
specialise_filter_list = consumption_specialise_list + specialise_list
def test_for_subscription(movement, causality):
instance_tree = movement.getAggregateValue(portal_type="Instance Tree")
......
......@@ -421,42 +421,39 @@ class TestHostingSubscriptionSimulation(SlapOSTestCaseMixin):
def _prepare(self):
person = self.portal.person_module.template_member\
.Base_createCloneDocument(batch_mode=1)
self.subscription = self.portal.hosting_subscription_module.newContent()
self.instance_tree = self.portal.instance_tree_module\
.template_instance_tree.Base_createCloneDocument(batch_mode=1)
self.initial_date = DateTime('2011/02/16')
stop_date = DateTime('2011/04/16')
self.subscription.edit(
periodicity_hour=0,
periodicity_minute=0,
periodicity_month_day=self.initial_date.day(),
destination_section=person.getRelativeUrl()
self.instance_tree.edit(
destination_section_value=person
)
self.portal.portal_workflow._jumpToStateFor(self.subscription, 'validated')
self.portal.portal_workflow._jumpToStateFor(self.instance_tree, 'validated')
open_order = self.instance_tree.InstanceTree_requestUpdateOpenSaleOrder(
specialise="sale_trade_condition_module/couscous_trade_condition"
)
self.initial_date = DateTime('2011/02/16')
stop_date = DateTime('2011/04/16')
open_sale_order_template = self.portal.restrictedTraverse(
self.portal.portal_preferences.getPreferredOpenSaleOrderTemplate())
open_sale_order_line_template = self.portal.restrictedTraverse(
self.portal.portal_preferences.getPreferredOpenSaleOrderLineTemplate())
open_order = open_sale_order_template.Base_createCloneDocument(
batch_mode=1)
open_order.edit(
destination_decision=person.getRelativeUrl(),
destination_section=person.getRelativeUrl(),
destination=person.getRelativeUrl(),
effective_date=self.initial_date,
expiration_date=self.initial_date + 1,
expiration_date=self.initial_date + 1
)
self.portal.portal_workflow._jumpToStateFor(open_order, 'validated')
self.open_order_line = open_sale_order_line_template.Base_createCloneDocument(
batch_mode=1, destination=open_order)
self.open_order_line = open_order.contentValues(
portal_type='Open Sale Order Line'
)[0]
self.open_order_line.edit(
aggregate=self.subscription.getRelativeUrl(),
start_date=self.initial_date,
stop_date=stop_date
)
self.subscription = self.open_order_line.getAggregateValue(portal_type='Hosting Subscription')
self.subscription.edit(
periodicity_hour=0,
periodicity_minute=0,
periodicity_month_day=self.initial_date.day(),
)
self.tic()
applied_rule_list = self.portal.portal_catalog(portal_type='Applied Rule',
......
......@@ -8,6 +8,7 @@ business_process_module/slapos_subscription_business_process
business_process_module/slapos_subscription_business_process/**
currency_module/CNY
currency_module/EUR
sale_trade_condition_module/couscous_trade_condition
sale_trade_condition_module/slapos_aggregated_consumption_trade_condition
sale_trade_condition_module/slapos_aggregated_consumption_trade_condition/**
sale_trade_condition_module/slapos_aggregated_subscription_trade_condition
......
......@@ -22,9 +22,6 @@ business_process_module/slapos_subscription_business_process
business_process_module/slapos_subscription_business_process/**
currency_module/CNY
currency_module/EUR
open_sale_order_module/slapos_accounting_open_sale_order_line_template
open_sale_order_module/slapos_accounting_open_sale_order_line_template/slapos_accounting_open_sale_order_line_template
open_sale_order_module/slapos_accounting_open_sale_order_template
organisation_module/slapos
organisation_module/slapos/bank_account
portal_alarms/slapos_accounting_generate_packing_list_from_tioxml
......@@ -75,6 +72,8 @@ portal_solvers/Automatic Quantity Adopt Solver/**
sale_packing_list_module/slapos_accounting_instance_delivery_line_template
sale_packing_list_module/slapos_accounting_instance_delivery_line_template/**
sale_packing_list_module/slapos_accounting_instance_delivery_template
sale_trade_condition_module/couscous_trade_condition
sale_trade_condition_module/couscous_trade_condition/**
sale_trade_condition_module/slapos_aggregated_consumption_trade_condition
sale_trade_condition_module/slapos_aggregated_consumption_trade_condition/**
sale_trade_condition_module/slapos_aggregated_subscription_trade_condition
......
......@@ -104,8 +104,6 @@
<key> <string>left</string> </key>
<value>
<list>
<string>my_preferred_open_sale_order_line_template</string>
<string>my_preferred_open_sale_order_template</string>
<string>my_preferred_zh_pre_payment_template</string>
<string>my_preferred_default_pre_payment_template</string>
<string>my_preferred_instance_delivery_template</string>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_preferred_open_sale_order_line_template</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Preferred Open Sale Order Line Template</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_preferred_open_sale_order_template</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Preferred Open Sale Order Template</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -569,9 +569,15 @@ class DefaultScenarioMixin(TestSlapOSSecurityMixin):
aggregated_delivery_list = self.portal.portal_catalog(
portal_type='Sale Packing List',
default_destination_section_uid=person.getUid(),
specialise_uid=self.portal.restrictedTraverse(self.portal\
specialise_uid=[self.portal.restrictedTraverse(self.portal\
.portal_preferences.getPreferredAggregatedSaleTradeCondition()\
).getUid()
] + [
i.uid for i in self.portal.ERP5Site_searchRelatedInheritedSpecialiseList(
specialise_uid=self.portal.restrictedTraverse(self.portal
.portal_preferences.getPreferredAggregatedSaleTradeCondition()
).getUid())
]
)
if len(subscription_list) == 0:
......
......@@ -705,10 +705,6 @@ class SlapOSTestCaseMixin(testSlapOSMixin):
'accounting_module/slapos_pre_payment_template',
preferred_instance_delivery_template=\
'sale_packing_list_module/slapos_accounting_instance_delivery_template',
preferred_open_sale_order_line_template=\
'open_sale_order_module/slapos_accounting_open_sale_order_line_template/slapos_accounting_open_sale_order_line_template',
preferred_open_sale_order_template=\
'open_sale_order_module/slapos_accounting_open_sale_order_template',
preferred_zh_pre_payment_template=\
'accounting_module/slapos_wechat_pre_payment_template',
preferred_zh_pre_payment_subscription_invoice_template=\
......@@ -717,6 +713,10 @@ class SlapOSTestCaseMixin(testSlapOSMixin):
'accounting_module/template_pre_payment_subscription_sale_invoice_transaction'
)
self.portal.sale_trade_condition_module.couscous_trade_condition.edit(
source_section='organisation_module/slapos',
source='organisation_module/slapos'
)
self.tic()
def redefineAccountingTemplatesonPreferences(self, price_currency="currency_module/EUR"):
......@@ -764,13 +764,8 @@ class SlapOSTestCaseMixin(testSlapOSMixin):
source_value=organisation
)
open_sale_order_module = self.portal.open_sale_order_module
preferred_open_sale_order_template=\
open_sale_order_module.slapos_accounting_open_sale_order_template.Base_createCloneDocument(batch_mode=1)
preferred_open_sale_order_template.edit(
source_section_value = organisation,
self.portal.sale_trade_condition_module.couscous_trade_condition.edit(
source_section_value=organisation,
source_value=organisation
)
......@@ -784,9 +779,7 @@ class SlapOSTestCaseMixin(testSlapOSMixin):
preferred_default_pre_payment_subscription_invoice_template=\
preferred_default_pre_payment_subscription_invoice_template.getRelativeUrl(),
preferred_instance_delivery_template=\
preferred_instance_delivery_template.getRelativeUrl(),
preferred_open_sale_order_template=\
preferred_open_sale_order_template.getRelativeUrl()
preferred_instance_delivery_template.getRelativeUrl()
)
self.tic()
......@@ -842,12 +835,7 @@ class SlapOSTestCaseMixin(testSlapOSMixin):
source_value=fr_organisation
)
open_sale_order_module = self.portal.open_sale_order_module
preferred_open_sale_order_template=\
open_sale_order_module.slapos_accounting_open_sale_order_template.Base_createCloneDocument(batch_mode=1)
preferred_open_sale_order_template.edit(
self.portal.sale_trade_condition_module.couscous_trade_condition.edit(
source_section_value=fr_organisation,
source_value=fr_organisation
)
......@@ -862,9 +850,7 @@ class SlapOSTestCaseMixin(testSlapOSMixin):
preferred_default_pre_payment_subscription_invoice_template=\
preferred_default_pre_payment_subscription_invoice_template.getRelativeUrl(),
preferred_instance_delivery_template=\
preferred_instance_delivery_template.getRelativeUrl(),
preferred_open_sale_order_template=\
preferred_open_sale_order_template.getRelativeUrl()
preferred_instance_delivery_template.getRelativeUrl()
)
self.tic()
......
......@@ -4,6 +4,7 @@
<item>follow_up</item>
<item>source</item>
<item>source_section</item>
<item>specialise</item>
</portal_type>
<portal_type id="Subscription Condition Module">
<item>business_application</item>
......
......@@ -16,9 +16,7 @@
<item>Price</item>
<item>Reference</item>
<item>SlaposSubscriptionRequestAuditConstraint</item>
<item>SoftwareInstance</item>
<item>Task</item>
<item>TextDocument</item>
<item>Url</item>
<item>VariationRange</item>
</portal_type>
......
......@@ -82,6 +82,7 @@
<string>my_title</string>
<string>my_reference</string>
<string>my_short_title</string>
<string>my_specialise_title</string>
<string>my_description</string>
<string>my_source_section_title</string>
</list>
......
......@@ -12,7 +12,6 @@
<list>
<string>base_category</string>
<string>catalog_index</string>
<string>editable</string>
<string>portal_type</string>
<string>title</string>
</list>
......@@ -20,7 +19,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_source_section_title</string> </value>
<value> <string>my_specialise_title</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
......@@ -77,16 +76,12 @@
<dictionary>
<item>
<key> <string>base_category</string> </key>
<value> <string>source_section</string> </value>
<value> <string>specialise</string> </value>
</item>
<item>
<key> <string>catalog_index</string> </key>
<value> <string>title</string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_relation_field</string> </value>
......@@ -100,12 +95,8 @@
<value>
<list>
<tuple>
<string>Person</string>
<string>Person</string>
</tuple>
<tuple>
<string>Organisation</string>
<string>Organisation</string>
<string>Sale Trade Condition</string>
<string>Sale Trade Condition</string>
</tuple>
</list>
</value>
......@@ -116,7 +107,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Supplier</string> </value>
<value> <string>Sale Trade Condition</string> </value>
</item>
</dictionary>
</value>
......
......@@ -70,22 +70,28 @@ contract = None
if token:
contract = person.Person_applyContractInvitation(invitation_token)
subscription_condition = context.portal_catalog.getResultValue(
portal_type="Subscription Condition",
reference=subscription_reference if (target_language != "zh") else (subscription_reference + '_zh'),
validation_state="validated"
)
subscription_request = context.subscription_request_module.newContent(
portal_type="Subscription Request",
destination_section_value=person,
quantity=user_input_dict["amount"],
language=target_language
language=target_language,
specialise_value=subscription_condition,
)
subscription_request.setDefaultEmailText(email)
def wrapWithShadow(subscription_request, subscription_reference,
def wrapWithShadow(subscription_request,
subscription_request_id, variation_reference, contract=contract):
subscription_request.activate(tag="subscription_condition_%s" % subscription_request_id
).SubscriptionRequest_applyCondition(subscription_reference, target_language)
).SubscriptionRequest_applyCondition()
return subscription_request.SubscriptionRequest_requestPaymentTransaction(
tag="subscription_%s" % subscription_request_id,
target_language=target_language,
contract=contract,
variation_reference=variation_reference
)
......@@ -94,9 +100,11 @@ payment = person.Person_restrictMethodAsShadowUser(
shadow_document=person,
callable_object=wrapWithShadow,
argument_list=[subscription_request,
subscription_reference, subscription_request.getId(),
subscription_request.getId(),
variation_reference, contract])
# Ensure tests crashes in batch mode
assert payment.getPaymentMode() in ['payzen', 'wechat']
if batch_mode:
return {'subscription' : subscription_request.getRelativeUrl(),
'payment': payment.getRelativeUrl() }
......@@ -104,10 +112,12 @@ if batch_mode:
def wrapRedirectWithShadow(payment_transaction, web_site):
# getTotalPayble returns a negative value
if payment_transaction.PaymentTransaction_getTotalPayablePrice() < 0:
if target_language == "zh":
if payment.getPaymentMode() == 'wechat':
return payment_transaction.PaymentTransaction_redirectToManualWechatPayment(web_site)
else:
elif payment.getPaymentMode() == 'payzen':
return payment_transaction.PaymentTransaction_redirectToManualPayzenPayment(web_site)
else:
raise NotImplementedError('Not supported payment mode (%s) for %s' % (payment.getPaymentMode(), payment.getRelativeUrl()))
return payment_transaction.PaymentTransaction_redirectToManualFreePayment(web_site)
return person.Person_restrictMethodAsShadowUser(
......
......@@ -106,8 +106,8 @@
<string>Title</string>
</tuple>
<tuple>
<string>url_string</string>
<string>Url String</string>
<string>specialise_title</string>
<string>Subscription Condition</string>
</tuple>
<tuple>
<string>start_date</string>
......
......@@ -2,51 +2,31 @@ from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
# Set AcceptLanguage in the REQUEST so that getDefaultLanguage() can work
if target_language and context.REQUEST.get('AcceptLanguage'):
context.REQUEST['AcceptLanguage'].set(target_language, 10)
if context.getSimulationState() not in ["draft", "planned"]:
# Don't modify it anymore
return
if subscription_condition_reference is not None:
# It would be better use some clever API here.
if target_language == "zh":
subscription_condition_reference += "_zh"
subscription_condition = context.portal_catalog.getResultValue(
portal_type="Subscription Condition",
reference=subscription_condition_reference,
validation_state="validated")
else:
subscription_condition = context.getSpecialiseValue()
subscription_condition = context.getSpecialiseValue()
if subscription_condition is None:
raise ValueError(
"It was not possible to find the appropriate Condition %s for this Subscription" \
% subscription_condition_reference)
"It was not possible to find the appropriate Condition for this Subscription")
instance_xml = subscription_condition.SubscriptionCondition_renderParameter(
amount=int(context.getQuantity()))
# Get Subscription condition for this Subscription Request
email = context.getDestinationSectionValue().getDefaultEmailText()
now = DateTime()
context.edit(
title="Subscription %s for %s" % (subscription_condition.getTitle(), email),
url_string=subscription_condition.getUrlString(),
text_content=instance_xml,
sla_xml=subscription_condition.getSlaXml(),
start_date=now,
root_slave=subscription_condition.getRootSlave(),
title="Subscription %s for %s" % (subscription_condition.getTitle(), context.getDestinationSectionValue().getDefaultEmailText()),
#url_string=subscription_condition.getUrlString(),
#text_content=instance_xml,
#sla_xml=subscription_condition.getSlaXml(),
start_date=DateTime(),
#root_slave=subscription_condition.getRootSlave(),
specialise_value=subscription_condition,
price=subscription_condition.getPrice(),
price_currency=subscription_condition.getPriceCurrency(),
#price=subscription_condition.getPrice(),
#price_currency=subscription_condition.getPriceCurrency(),
# Set Provider from Subscription Condition
source=subscription_condition.getSource(),
source_section=subscription_condition.getSourceSection()
#source=subscription_condition.getSource(),
#source_section=subscription_condition.getSourceSection()
source_reference=subscription_condition.getSourceReference()
)
context.setSourceReference(subscription_condition.getSourceReference())
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>subscription_condition_reference=None, target_language=None, REQUEST=None</string> </value>
<value> <string>REQUEST=None</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
......
......@@ -29,8 +29,9 @@ if sale_packing_list_list:
portal_type="Sale Invoice Transaction",
strict_causality_uid=[x.getUid() for x in sale_packing_list_list],
)
subscription_currency = subscription_request.getPriceCurrency()
subscription_source_section = subscription_request.getSourceSection()
trade_condition = subscription_request.getSpecialiseValue().getSpecialiseValue()
subscription_currency = trade_condition.getPriceCurrency()
subscription_source_section = trade_condition.getSourceSection()
for sale_invoice in sale_invoice_list:
sale_invoice = sale_invoice.getObject()
if sale_invoice.getPriceCurrency() != subscription_currency:
......
......@@ -6,49 +6,67 @@ portal = context.getPortalObject()
current_invoice = context.getCausalityValue()
if current_invoice is None:
# _init is guarded by the owner role
# but SubscriptionRequest_createRelatedSaleInvoiceTransaction is called with a shadow user
# leading to unauthorized error
# Instead, clone one temp invoice (as there is no _afterClone)
invoice_template = portal.restrictedTraverse(template)
current_invoice = invoice_template.Base_createCloneDocument(batch_mode=1)
assert current_invoice is not None
current_invoice.manage_delObjects([x for x in
current_invoice.contentIds() if x != "1"])
current_invoice.edit(
categories=[],
title=None
)
current_invoice["1"].manage_delObjects([x for x in
current_invoice["1"].contentIds()])
current_invoice["1"].edit(
categories=[],
title=None,
quantity=None,
price=None,
base_unit_price=None,
#index=None,
#variation_base_category_list=None,
#variation_category_list=None,
)
current_invoice["1"].updateCellRange('movement')
subscription_trade_condition = portal.portal_preferences.getPreferredAggregatedSubscriptionSaleTradeCondition()
user_trade_condition = context.getDestinationSectionValue().\
Person_getAggregatedSubscriptionSaleTradeConditionValue(subscription_trade_condition)
if user_trade_condition:
current_invoice.setSpecialise(user_trade_condition)
context.edit(causality_value=current_invoice)
payment_transaction = portal.restrictedTraverse(payment)
current_invoice.edit(
title="Reservation Fee",
destination_value=context.getDestinationSection(),
destination_section_value=context.getDestinationSection(),
destination_decision_value=context.getDestinationSection(),
start_date=payment_transaction.getStartDate(),
stop_date=payment_transaction.getStopDate(),
)
title="Reservation Fee",
destination_value=context.getDestinationSection(),
destination_section_value=context.getDestinationSection(),
destination_decision_value=context.getDestinationSection(),
start_date=payment_transaction.getStartDate(),
stop_date=payment_transaction.getStopDate(),
current_invoice["1"].edit(
start_date=payment_transaction.getStartDate(),
stop_date=payment_transaction.getStopDate())
if service_variation is not None:
current_invoice["1"].setVariation(service_variation)
cell = current_invoice["1"]["movement_0"]
cell.edit(
variation=current_invoice["1"].getVariation(),
quantity=context.getQuantity()
specialise=portal.portal_preferences.getPreferredAggregatedSubscriptionSaleTradeCondition(),
source_section_value=payment_transaction.getSourceSectionValue(),
source_value=payment_transaction.getSourceValue(),
payment_mode_value=payment_transaction.getPaymentModeValue(),
price_currency_value=payment_transaction.getPriceCurrencyValue(),
resource_value=payment_transaction.getResourceValue(),
)
cell.setPrice(price)
# Test to see if the user has specific trade condition for aggregation.
person = context.getDestinationSectionValue()
trade_condition = person.Person_getAggregatedSubscriptionSaleTradeConditionValue(
current_invoice.getSpecialise()
current_invoice["1"].edit(
start_date=payment_transaction.getStartDate(),
stop_date=payment_transaction.getStopDate(),
use="trade/sale",
resource="service_module/slapos_reservation_fee_2",
quantity_unit="unit/piece",
base_contribution_list=[
"base_amount/invoicing/discounted",
"base_amount/invoicing/taxable"
],
quantity=context.getQuantity(),
price=price
)
if trade_condition != current_invoice.getSpecialise():
current_invoice.edit(specialise=trade_condition)
comment = "Validation invoice for subscription request %s" % context.getRelativeUrl()
current_invoice.plan(comment=comment)
......
......@@ -39,7 +39,8 @@ if payment_transaction is None or payment_transaction.getSimulationState() != "s
price_without_tax = None
for invoice_line in sale_invoice_transaction.objectValues(portal_type="Invoice Line"):
if invoice_line.getResource() == "service_module/slapos_reservation_fee":
# XXX
if invoice_line.getResource() == "service_module/slapos_reservation_fee_2":
price_without_tax = invoice_line.getTotalPrice()
assert price_without_tax is not None, "Something is wrong since price wasn't found"
......
......@@ -10,7 +10,7 @@ else:
business_process_uid_list = [
portal.business_process_module.slapos_subscription_business_process.getUid()]
subscription_delivery_specialise_uid_list = [q.getUid() for q in portal.portal_catalog(
subscription_delivery_specialise_uid_list = [q.getUid() for q in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
specialise_uid=business_process_uid_list, portal_type='Sale Trade Condition')]
# Recover all Aggregated Sale Packing Lists
......
......@@ -13,7 +13,11 @@ if instance_tree is None:
if instance_tree is not None:
if instance_tree.getCausalityState() == "diverged":
# Call it as soon as possible
instance_tree.InstanceTree_requestUpdateOpenSaleOrder()
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()
......
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
......@@ -14,17 +18,19 @@ default_xml = """<?xml version="1.0" encoding="utf-8"?>
</instance>
"""
if context.getUrlString() is None:
if subscription_condition.getUrlString() is None:
raise ValueError("url_string cannot be None")
request_kw.update(
software_release=context.getUrlString(),
software_release=subscription_condition.getUrlString(),
# Bad title
software_title=context.getTitle() + " %s" % str(context.getUid()),
software_type=context.getSourceReference("default"),
instance_xml=context.getTextContent(default_xml).strip(),
sla_xml=context.getSlaXml(default_xml).strip(),
shared=bool(context.getRootSlave(0)),
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",
)
......
......@@ -9,88 +9,143 @@ current_payment = None
service_variation = None
if current_invoice is None:
if target_language == "zh": # Wechat payment, reservation fee is 188 CNY
payment_template = portal.restrictedTraverse(portal.portal_preferences.getPreferredZhPrePaymentTemplate())
invoice_template = portal.restrictedTraverse(portal.portal_preferences.getPreferredZhPrePaymentSubscriptionInvoiceTemplate())
else: # Payzen payment, reservation fee is 25 EUR
payment_template = portal.restrictedTraverse(portal.portal_preferences.getPreferredDefaultPrePaymentTemplate())
invoice_template = portal.restrictedTraverse(portal.portal_preferences.getPreferredDefaultPrePaymentSubscriptionInvoiceTemplate())
# PaymentTransaction_init is guarded by the owner role
# but SubscriptionRequest_requestPaymentTransaction is called with a shadow user
# leading to unauthorized error
# Instead, clone one temp payment (as there is no PaymentTransaction_afterClone)
payment_template = portal.restrictedTraverse(portal.portal_preferences.getPreferredDefaultPrePaymentTemplate())
current_payment = payment_template.Base_createCloneDocument(batch_mode=1)
assert current_payment is not None
current_payment.manage_delObjects([x for x in
current_payment.contentIds()])
current_payment.edit(
title="Payment for Reservation Fee",
destination_value=context.getDestinationSection(),
destination_section_value=context.getDestinationSection(),
destination_decision_value=context.getDestinationSection(),
start_date=DateTime(),
stop_date=DateTime()
)
categories=[],
title=None
)
subscription_condition = context.getSpecialiseValue(portal_type='Subscription Condition')
trade_condition = subscription_condition.getSpecialiseValue(portal_type='Sale Trade Condition')
assert trade_condition is not None
# XXX if we have a tree of trade condition, getPaymentMode may be empty (if there is no acquisition)
payment_mode = trade_condition.getPaymentModeValue()
now = DateTime()
current_payment.edit(
title="Payment for Reservation Fee",
specialise_value=trade_condition,
destination_value=context.getDestinationSection(),
destination_section_value=context.getDestinationSection(),
destination_decision_value=context.getDestinationSection(),
start_date=now,
stop_date=now,
payment_mode_uid=payment_mode.getUid(),
#source_payment_value=trade_condition.getSourcePaymentValue(),
#source_value=trade_condition.getSourceValue(),
#source_section_value=trade_condition.getSourceSectionValue(),
#price_currency_value=trade_condition.getPriceCurrencyValue(),
#resource=
)
current_payment.SaleOrder_applySaleTradeCondition(batch_mode=1, force=1)
current_payment.edit(
specialise_value=None,
)
# Search for matching resource
service = portal.portal_catalog(
# XXX Hardcoded as temporary
id='slapos_reservation_fee_2',
portal_type='Service',
validation_state='validated',
use__relative_url='use/trade/sale'
)[0].getObject()
amount = context.getQuantity()
if context.SubscriptionRequest_testSkippedReservationFree(contract):
# Reservation is Free
price = 0
tax = 0
else:
invoice_line = invoice_template["1"].asContext(
destination_section=context.getDestinationSection(),
start_date=DateTime(),
stop_date=DateTime()
tmp_invoice = portal.accounting_module.newContent(
temp_object=True,
portal_type='Sale Invoice Transaction',
price_currency_value=trade_condition.getPriceCurrencyValue(),
specialise=portal.portal_preferences.getPreferredAggregatedSubscriptionSaleTradeCondition(),
)
tmp_invoice_line = tmp_invoice.newContent(
temp_object=True,
portal_type='Invoice Line',
resource_value=service,
quantity=context.getQuantity(),
quantity_unit=service.getQuantityUnit(),
base_contribution_list=service.getBaseContributionList(),
use=service.getUse(),
# Dates are required to correctly select the correct trade condition version (based on effective date)
start_date=now,
stop_date=now,
)
subscription_trade_condition = portal.portal_preferences.getPreferredAggregatedSubscriptionSaleTradeCondition()
user_trade_condition = context.getDestinationSectionValue().\
Person_getAggregatedSubscriptionSaleTradeConditionValue(subscription_trade_condition)
if user_trade_condition:
invoice_line.edit(
specialise=user_trade_condition,
destination_section=context.getDestinationSection())
resource = invoice_line.getResourceValue()
if variation_reference is not None:
for variation in resource.objectValues(portal_type="Service Individual Variation"):
if variation.getReference() == variation_reference:
service_variation = variation.getRelativeUrl()
invoice_line.setVariation(service_variation)
price = invoice_line.getResourceValue().getPrice(
context=invoice_line)
predicate_list = []
inherited_trade_condition = trade_condition
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=tmp_invoice_line,
predicate_list=predicate_list,
default=None
)
if price is None:
raise NotImplementedError('Price must be defined')
# XXX calculate Tax
# We need to provide Price to pay right the way, so we need to include
# taxation at this point it is most liketly to quickly forecast price
# with taxes, but for now it is hardcoded.
tax = 0
if 'base_amount/invoicing/taxable' in invoice_line.getBaseContributionList():
specialise = invoice_line.getSpecialiseValue(portal_type='Sale Trade Condition')
if specialise is not None:
for trade_model_line in specialise.getAggregatedAmountList(invoice_line):
tax = trade_model_line.getPrice()
# For simplification consider tax is a single value.
break
for line in current_payment.contentValues():
if line.getSource() == "account_module/payment_to_encash":
total = round((-int(amount) * price)+(-int(amount) * price*tax), 2)
line.setQuantity(total)
elif line.getSource() == "account_module/receivable":
total = round((int(amount) * price)+(int(amount) * price*tax), 2)
line.setQuantity(total)
tax = None
if 'base_amount/invoicing/taxable' in tmp_invoice_line.getBaseContributionList():
for trade_model_line in tmp_invoice_line.getAggregatedAmountList():
tax = trade_model_line.getPrice()
# For simplification consider tax is a single value.
break
if tax is None:
raise NotImplementedError('No tax trade model line found')
amount = context.getQuantity()
total = round((int(amount) * price)+(int(amount) * price*tax), 2)
current_payment.newContent(
portal_type="Accounting Transaction Line",
quantity=total,
destination="account_module/payable",
source="account_module/receivable",
)
current_payment.newContent(
portal_type="Accounting Transaction Line",
quantity=-total,
# XXX why source/destination are identical?
destination="account_module/payment_to_encash",
source="account_module/payment_to_encash",
)
# Accelarate job of alarms before proceed to payment.
comment = "Validation payment for subscription request %s" % context.getRelativeUrl()
current_payment.confirm(comment=comment)
current_payment.start(comment=comment)
if not price:
current_payment.stop(comment="%s (Free)" % comment)
elif target_language != "zh":
# Payzen don't require update like this.
elif current_payment.getPaymentMode() == "payzen":
# Payzen require update like this.
current_payment.PaymentTransaction_updateStatus()
current_payment.reindexObject(activate_kw={'tag': tag})
context.reindexObject(activate_kw={'tag': tag})
context.activate(tag=tag).SubscriptionRequest_createRelatedSaleInvoiceTransaction(
price, tag, current_payment.getRelativeUrl(), invoice_template.getRelativeUrl(),
price, tag, current_payment.getRelativeUrl(), portal.portal_preferences.getPreferredDefaultPrePaymentSubscriptionInvoiceTemplate(),
service_variation)
return current_payment
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>tag, target_language, contract=None, variation_reference=None, REQUEST=None</string> </value>
<value> <string>tag, contract=None, variation_reference=None, REQUEST=None</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
......
......@@ -12,9 +12,9 @@ specialise_uid = portal.restrictedTraverse(
trade_condition_uid_list = [specialise_uid]
trade_condition_uid_list.extend([
i.uid for i in portal.portal_catalog(
i.uid for i in portal.ERP5Site_searchRelatedInheritedSpecialiseList(
portal_type="Sale Trade Condition",
specialise__uid=specialise_uid,
specialise_uid=specialise_uid,
validation_state="validated")])
# This is normally one, but we navegate in case
......
......@@ -61,13 +61,7 @@
<item>
<key> <string>center</string> </key>
<value>
<list>
<string>my_root_slave</string>
<string>my_source_reference</string>
<string>my_url_string</string>
<string>my_text_content</string>
<string>my_sla_xml</string>
</list>
<list/>
</value>
</item>
<item>
......@@ -90,7 +84,6 @@
<string>my_specialise_title_list</string>
<string>my_causality_title_list</string>
<string>my_destination_section_title_list</string>
<string>my_source_section_title</string>
</list>
</value>
</item>
......@@ -99,8 +92,6 @@
<value>
<list>
<string>my_quantity</string>
<string>my_price</string>
<string>my_price_currency</string>
<string>my_start_date</string>
<string>my_stop_date</string>
<string>my_translated_simulation_state_title</string>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>editable</string>
<string>enabled</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_price</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_price</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewTradeFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>editable</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_price_currency</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_price_currency</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewTradeFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>display_width</string>
<string>editable</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_root_slave</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Shared</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>editable</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_sla_xml</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_text_area_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Root SLA XML</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_source_reference</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_read_only_reference</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Software Type</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>editable</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_text_content</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_text_area_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Root XML</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>display_width</string>
<string>editable</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_url_string</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Software Release</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -31,18 +31,20 @@ class testSlapOSSubscriptionDualOrganisationScenario(TestSlapOSSubscriptionScena
self.expected_source_section = fr_organisation.getRelativeUrl()
self.expected_zh_reservation_fee = 188.00
self.subscription_condition.edit(
self.subscription_condition.getSpecialiseValue().edit(
source=self.expected_source,
source_section=self.expected_source_section
source_section=self.expected_source_section,
source_payment=self.expected_source_section + '/bank_account',
)
self.subscription_condition_zh = self.createChineseSubscriptionCondition()
self.expected_zh_source = zh_organisation.getRelativeUrl()
self.expected_zh_source_section = zh_organisation.getRelativeUrl()
self.subscription_condition_zh.edit(
self.subscription_condition_zh.getSpecialiseValue().edit(
source=self.expected_zh_source,
source_section=self.expected_zh_source_section
source_section=self.expected_zh_source_section,
source_payment=self.expected_zh_source_section + '/bank_account',
)
self.portal.portal_caches.clearAllCache()
......@@ -91,11 +93,12 @@ class testSlapOSSubscriptionDualOrganisationScenario(TestSlapOSSubscriptionScena
self.assertEqual(language,
subscription_request.getLanguage())
trade_condition = subscription_request.getSpecialiseValue().getSpecialiseValue()
self.assertEqual(expected_price_currency,
subscription_request.getPriceCurrency())
trade_condition.getPriceCurrency())
self.assertEqual(expected_source_section,
subscription_request.getSourceSection())
trade_condition.getSourceSection())
self.checkDraftSubscriptionRequest(subscription_request,
......@@ -145,12 +148,13 @@ class testSlapOSSubscriptionDualOrganisationScenario(TestSlapOSSubscriptionScena
self.assertEqual(len(sale_packing_list_list), 1)
sale_packing_list = sale_packing_list_list[0]
trade_condition = subscription_request.getSpecialiseValue().getSpecialiseValue()
self.assertEqual(sale_packing_list.getPriceCurrency(),
subscription_request.getPriceCurrency())
trade_condition.getPriceCurrency())
self.assertEqual(sale_packing_list.getSpecialise(),
"sale_trade_condition_module/slapos_reservation_refund_trade_condition")
if subscription_request.getPriceCurrency() == "currency_module/CNY":
if trade_condition.getPriceCurrency() == "currency_module/CNY":
expected_reservation_fee = self.expected_zh_reservation_fee_without_tax
else:
expected_reservation_fee = self.expected_reservation_fee_without_tax
......@@ -189,7 +193,8 @@ class testSlapOSSubscriptionDualOrganisationScenario(TestSlapOSSubscriptionScena
if not self.expected_free_reservation:
for subscription_request in subscription_request_list:
if subscription_request.getPriceCurrency() == "currency_module/CNY":
trade_condition = subscription_request.getSpecialiseValue().getSpecialiseValue()
if trade_condition.getPriceCurrency() == "currency_module/CNY":
self.checkAndPayFirstMonthViaWechat(subscription_request)
else:
self.checkAndPayFirstMonth(subscription_request)
......
......@@ -31,11 +31,10 @@ class testSlapOSSubscriptionNewTemplateChineseScenario(TestSlapOSSubscriptionChi
self.expected_source = organisation.getRelativeUrl()
self.expected_source_section = organisation.getRelativeUrl()
# Set those values (source and source section) are only meaninfull if
# the templates on preferences differ from Chinese and European organisations.
self.subscription_condition.edit(
source=None,
source_section=None
self.subscription_condition.getSpecialiseValue().edit(
source=self.expected_source,
source_section=self.expected_source_section,
source_payment=self.expected_source_section + '/bank_account',
)
self.portal.portal_caches.clearAllCache()
......
......@@ -30,11 +30,10 @@ class testSlapOSSubscriptionNewTemplateScenario(TestSlapOSSubscriptionScenarioMi
self.expected_source = organisation.getRelativeUrl()
self.expected_source_section = organisation.getRelativeUrl()
# Set those values (source and source section) are only meaninfull if
# the templates on preferences differ from Chinese and European organisations.
self.subscription_condition.edit(
source=None,
source_section=None
self.subscription_condition.getSpecialiseValue().edit(
source=self.expected_source,
source_section=self.expected_source_section,
source_payment=self.expected_source_section + '/bank_account',
)
self.portal.portal_caches.clearAllCache()
......
......@@ -28,21 +28,26 @@ class testSlapOSSubscriptionPerUserTradeConditionScenario(TestSlapOSSubscription
root_trade_condition = self.portal.portal_preferences.\
getPreferredAggregatedSubscriptionSaleTradeCondition()
root_trade_condition_value = self.portal.restrictedTraverse(
root_trade_condition)
user_trade_condition = root_trade_condition_value.\
Base_createCloneDocument(batch_mode=1)
user_trade_condition.manage_delObjects([x for x in
user_trade_condition.contentIds()])
user_trade_condition.edit(
title="TEST Trade Condition for %s" % person.getTitle(),
reference="%s_custom_%s" % (user_trade_condition.getReference(), user_trade_condition.getUid()),
destination_section_value=person,
specialise=root_trade_condition
specialise_value=self.subscription_condition.getSpecialiseValue(),
payment_mode='payzen',
price_currency_value=self.subscription_condition.getSpecialiseValue().getPriceCurrency(),
)
user_trade_condition.validate()
self.subscription_condition.edit(specialise_value=user_trade_condition)
return user_trade_condition
def _test_subscription_scenario_with_custom_condition(self, amount=1, language=None):
......@@ -87,21 +92,13 @@ class testSlapOSSubscriptionPerUserTradeConditionScenarioDetaxed(TestSlapOSSubsc
TestSlapOSSubscriptionScenarioMixin.afterSetUp(self)
self.expected_individual_price_without_tax = 195
self.expected_individual_price_without_tax = 162.5
self.expected_individual_price_with_tax = 195
self.expected_reservation_fee = 25.00
self.expected_reservation_fee_without_tax = 25
self.expected_reservation_quantity_tax = 25
self.expected_reservation_tax = 0.0
self.expected_price_currency = "currency_module/EUR"
self.expected_zh_individual_price_without_tax = 1888
self.expected_zh_individual_price_with_tax = 1888
self.expected_zh_reservation_fee = 188
self.expected_zh_reservation_fee_without_tax = 188
self.expected_zh_reservation_quantity_tax = 188
self.expected_zh_reservation_tax = 0.0
self.expected_reservation_fee_without_tax = 20.83
self.expected_reservation_quantity_tax = 20.83
self.expected_reservation_tax = 4.17
def createDetaxedUserSaleTradeCondition(self, person):
root_trade_condition = self.portal.portal_preferences.\
......@@ -113,16 +110,30 @@ class testSlapOSSubscriptionPerUserTradeConditionScenarioDetaxed(TestSlapOSSubsc
user_trade_condition = root_trade_condition_value.\
Base_createCloneDocument(batch_mode=1)
user_trade_condition.edit(
title="TEST Trade Condition for %s" % person.getTitle(),
reference="%s_detaxed_%s" % (user_trade_condition.getReference(), user_trade_condition.getUid()),
destination_section_value=person,
specialise=root_trade_condition
specialise_value=self.subscription_condition.getSpecialiseValue(),
payment_mode='payzen',
price_currency_value=self.subscription_condition.getSpecialiseValue().getPriceCurrency(),
)
user_trade_condition["1"].setPrice(0.0)
user_trade_condition.manage_delObjects([x for x in
user_trade_condition.contentIds()])
user_trade_condition.newContent(
portal_type="Sale Supply Line",
base_price=self.expected_reservation_fee_without_tax,
resource='service_module/slapos_reservation_fee_2',
)
user_trade_condition.newContent(
portal_type="Sale Supply Line",
base_price=self.expected_individual_price_with_tax,
resource='service_module/slapos_instance_subscription',
)
user_trade_condition.validate()
self.subscription_condition.edit(specialise_value=user_trade_condition)
return user_trade_condition
def _test_subscription_scenario_with_detaxed_condition(self, amount=1, language=None):
......
......@@ -46,6 +46,16 @@ class TestSubscriptionSkinsMixin(SlapOSTestCaseMixinWithAbort):
notification_message.validate()
return notification_message
def newSaleTradeCondition(self, **kw):
sale_trade_condition = self.portal.sale_trade_condition_module.newContent(
portal_type='Sale Trade Condition',
title="Test Sale Trade Condition %s" % self.new_id,
reference="TESTSALETRADECONDITION-%s" % self.new_id,
**kw
)
self.tic()
return sale_trade_condition
def newSubscriptionCondition(self, **kw):
subscription_condition = self.portal.subscription_condition_module.newContent(
portal_type='Subscription Condition',
......@@ -334,16 +344,13 @@ class TestSubscriptionRequest_applyCondition(TestSubscriptionSkinsMixin):
def test_SubscriptionRequest_applyCondition_raises_unauthorized(self):
self.assertRaises(Unauthorized, self.portal.SubscriptionRequest_applyCondition, REQUEST=self.portal.REQUEST)
def test_SubscriptionRequest_applyCondition_raises_if_subscription_request_is_not_found(self):
def test_SubscriptionRequest_applyCondition_raises_if_no_subscription_request(self):
subscription_request = self.newSubscriptionRequest()
self.assertRaises(ValueError, subscription_request.SubscriptionRequest_applyCondition)
self.assertRaises(ValueError, subscription_request.SubscriptionRequest_applyCondition,
subscription_condition_reference="subscription_condition_reference")
def test_SubscriptionRequest_applyCondition(self):
person = self.makePerson()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person)
subscription_condition = self.newSubscriptionCondition(
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
......@@ -362,28 +369,24 @@ class TestSubscriptionRequest_applyCondition(TestSubscriptionSkinsMixin):
source_reference="test_for_test_123")
subscription_condition.validate()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
specialise_value=subscription_condition)
self.tic()
subscription_request.SubscriptionRequest_applyCondition(
subscription_condition_reference=subscription_condition.getReference())
subscription_request.SubscriptionRequest_applyCondition()
self.assertEqual("Subscription %s for %s" % (subscription_condition.getTitle(), person.getDefaultEmailText()),
subscription_request.getTitle())
self.assertEqual("https://%s/software.cfg" % self.new_id, subscription_request.getUrlString())
self.assertEqual("""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="oi">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""", subscription_request.getSlaXml())
self.assertEqual("""<?xml version="1.0" encoding="utf-8"?>
<instance>
<parameter id="xx">couscous</parameter>
<parameter id="zz">yy</parameter>
</instance>""", subscription_request.getTextContent())
self.assertEqual(None, subscription_request.getUrlString())
#self.assertRaises(AttributeError, subscription_request.getSlaXml)
#self.assertRaises(AttributeError, subscription_request.getTextContent)
self.assertNotEqual(subscription_request.getStartDate(), None)
self.assertEqual(subscription_request.getSpecialiseValue(), subscription_condition)
self.assertEqual(subscription_request.getRootSlave(), False)
self.assertEqual(subscription_request.getPrice(), 99.9)
self.assertEqual(subscription_request.getPriceCurrency(), "currency_module/EUR")
self.assertEqual(subscription_request.getRootSlave(), None)
self.assertEqual(subscription_request.getPrice(), None)
self.assertEqual(subscription_request.getPriceCurrency(), None)
self.assertEqual(subscription_request.getSourceReference(), "test_for_test_123")
class SubscriptionRequest_boostrapUserAccount(TestSubscriptionSkinsMixin):
......@@ -666,8 +669,7 @@ class SubscriptionRequest_processRequest(TestSubscriptionSkinsMixin):
def test_process_request(self):
person = self.makePerson()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
subscription_condition = self.newSubscriptionCondition(
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
......@@ -681,7 +683,11 @@ class SubscriptionRequest_processRequest(TestSubscriptionSkinsMixin):
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
subscription_condition.validate()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
specialise_value=subscription_condition
)
self.tic()
subscription_request.SubscriptionRequest_processRequest()
......@@ -1056,8 +1062,9 @@ class TestSubscriptionRequest_processOrdered(TestSubscriptionSkinsMixin):
def test_no_sale_invoice(self):
person = self.makePerson()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
subscription_condition = self.newSubscriptionCondition(
specialise='sale_trade_condition_module/slapos_subscription_trade_condition',
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
......@@ -1071,7 +1078,11 @@ class TestSubscriptionRequest_processOrdered(TestSubscriptionSkinsMixin):
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
subscription_condition.validate()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
specialise_value=subscription_condition
)
self.tic()
self.assertEqual(
......@@ -1132,8 +1143,8 @@ class TestSubscriptionRequest_processOrdered(TestSubscriptionSkinsMixin):
@simulate('SubscriptionRequest_verifyInstanceIsAllocated', '*args, **kwargs','return True')
def test_with_reservation_fee(self):
person = self.makePerson()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
subscription_condition = self.newSubscriptionCondition(
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
......@@ -1146,7 +1157,14 @@ class TestSubscriptionRequest_processOrdered(TestSubscriptionSkinsMixin):
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
source_reference="test_for_test_123",
specialise_value=self.newSaleTradeCondition()
)
subscription_condition.validate()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
specialise_value=subscription_condition
)
self.tic()
......@@ -1210,8 +1228,7 @@ class TestSubscriptionRequest_processOrdered(TestSubscriptionSkinsMixin):
@simulate('SubscriptionRequest_verifyInstanceIsAllocated', '*args, **kwargs','return True')
def test_confirmed(self):
person = self.makePerson()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
subscription_condition = self.newSubscriptionCondition(
url_string="https://%s/software.cfg" % self.new_id,
sla_xml="""<?xml version="1.0" encoding="utf-8"?>
<instance>
......@@ -1224,7 +1241,14 @@ class TestSubscriptionRequest_processOrdered(TestSubscriptionSkinsMixin):
<parameter id="zz">yy</parameter>
</instance>""",
root_slave=False,
source_reference="test_for_test_123")
source_reference="test_for_test_123",
specialise_value=self.newSaleTradeCondition()
)
subscription_condition.validate()
subscription_request = self.newSubscriptionRequest(
quantity=1, destination_section_value=person,
specialise_value=subscription_condition
)
subscription_request.plan()
subscription_request.order()
......
service_module/slapos_reservation_fee_2
\ No newline at end of file
service_module/slapos_reservation_fee_2
\ No newline at end of file
service_module/slapos_reservation_fee_2
\ No newline at end of file
......@@ -7,4 +7,5 @@ accounting_module/template_pre_payment_subscription_sale_invoice_transaction/**
accounting_module/template_wechat_pre_payment_subscription_sale_invoice_transaction
accounting_module/template_wechat_pre_payment_subscription_sale_invoice_transaction/**
portal_alarms/slapos_subscription_request_process_**
portal_alarms/slapos_update_subscription_request_consistency_state
\ No newline at end of file
portal_alarms/slapos_update_subscription_request_consistency_state
service_module/slapos_reservation_fee_2
......@@ -3,6 +3,7 @@ Subscription Condition | aggregate
Subscription Condition | follow_up
Subscription Condition | source
Subscription Condition | source_section
Subscription Condition | specialise
Subscription Request Module | business_application
Subscription Request | aggregate
Subscription Request | causality
......
......@@ -12,8 +12,6 @@ Subscription Request | Person
Subscription Request | Price
Subscription Request | Reference
Subscription Request | SlaposSubscriptionRequestAuditConstraint
Subscription Request | SoftwareInstance
Subscription Request | Task
Subscription Request | TextDocument
Subscription Request | Url
Subscription Request | VariationRange
\ 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