Commit f48bb9c4 authored by Romain Courteaud's avatar Romain Courteaud

slapos_panel: action to change a Subscription price

parent 1f83aee8
......@@ -272,6 +272,7 @@ def makeTestSlapOSCodingStyleTestCase(tested_business_template):
'slapos_panel/SoftwareInstance_getNewsDict',
'slapos_panel/SoftwareProduct_addSlapOSSoftwareRelease',
'slapos_panel/SoftwareProduct_addSlapOSSoftwareType',
'slapos_panel/SubscriptionRequest_changeSlaposSubmittedPrice',
'slapos_panel/SubscriptionRequest_jumpToPaymentPage',
'slapos_panel/Ticket_addSlapOSEvent',
'slapos_panel/Ticket_closeSlapOS',
......
......@@ -553,3 +553,329 @@ class TestSlapOSSubscriptionScenario(TestSlapOSSubscriptionScenarioMixin):
# 2 subscription requests
self.assertRelatedObjectCount(project, 24)
self.checkERP5StateBeforeExit()
def checkInstanceAllocationFromPayableToFree(self,
person_user_id, person_reference,
instance_title, software_release, software_type, server,
project_reference, deposit_amount, currency, sale_person):
self.login(person_user_id)
self.personRequestInstanceNotReady(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
project_reference=project_reference
)
self.tic()
instance_tree = self.portal.portal_catalog.getResultValue(
portal_type="Instance Tree",
title=instance_title,
follow_up__reference=project_reference
)
person = instance_tree.getDestinationSectionValue()
self.assertEqual(person.getUserId(), person_user_id)
subscription_request = self.checkServiceSubscriptionRequest(instance_tree, 'submitted')
self.assertEqual(subscription_request.getTotalPrice(), deposit_amount)
self.tic()
outstanding_amount_list = person.Entity_getOutstandingDepositAmountList(
currency.getUid(), ledger_uid=subscription_request.getLedgerUid())
self.assertEqual(sum([i.total_price for i in outstanding_amount_list]), deposit_amount)
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
self.logout()
self.login(sale_person.getUserId())
#previous_trade_condition = subscription_request.getSpecialiseValue()
subscription_request.SubscriptionRequest_changeFromPayableToFree(None)
self.tic()
self.logout()
self.login(person.getUserId())
# Ensure to pay from the website
#outstanding_amount = self.web_site.restrictedTraverse(outstanding_amount_list[0].getRelativeUrl())
#outstanding_amount.Base_createExternalPaymentTransactionFromOutstandingAmountAndRedirect()
self.tic()
self.logout()
self.login()
payment_transaction = self.portal.portal_catalog.getResultValue(
portal_type="Payment Transaction",
destination_section_uid=person.getUid(),
simulation_state="started"
)
self.assertEqual(payment_transaction, None)
self.login(person_user_id)
self.checkServiceSubscriptionRequest(instance_tree, 'cancelled')
amount = sum([i.total_price for i in person.Entity_getOutstandingDepositAmountList(
currency.getUid(), ledger_uid=subscription_request.getLedgerUid())])
self.assertEqual(0, amount)
self.login(person_user_id)
self.personRequestInstance(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
project_reference=project_reference
)
# now instantiate it on compute_node and set some nice connection dict
self.simulateSlapgridCP(server)
# let's find instances of user and check connection strings
instance_tree_list = [q.getObject() for q in
self._getCurrentInstanceTreeList()
if q.getTitle() == instance_title]
self.assertEqual(1, len(instance_tree_list))
instance_tree = instance_tree_list[0]
software_instance = instance_tree.getSuccessorValue()
self.assertEqual(software_instance.getTitle(),
instance_tree.getTitle())
connection_dict = software_instance.getConnectionXmlAsDict()
self.assertSameSet(('url_1', 'url_2'), connection_dict.keys())
self.login()
partition = software_instance.getAggregateValue()
self.assertSameSet(
['http://%s/' % q.getIpAddress() for q in
partition.contentValues(portal_type='Internet Protocol Address')],
connection_dict.values())
def test_virtual_master_with_accounting_scenario_from_payable_to_free(self):
with PinnedDateTime(self, DateTime('2024/02/17')):
currency, _, _, sale_person, _ = self.bootstrapVirtualMasterTest()
self.logout()
# lets join as slapos administrator, which will manager the project
project_owner_reference = 'project-%s' % self.generateNewId()
self.joinSlapOS(self.web_site, project_owner_reference)
self.login()
project_owner_person = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=project_owner_reference).getParentValue()
# owner_person.setCareerSubordinationValue(seller_organisation)
self.tic()
self.logout()
self.login(sale_person.getUserId())
project_relative_url = self.addProject(
is_accountable=True, person=project_owner_person, currency=currency)
self.logout()
self.login()
project = self.portal.restrictedTraverse(project_relative_url)
preference = self.portal.portal_preferences.slapos_default_system_preference
preference.edit(
preferred_subscription_assignment_category_list=[
'function/customer',
'role/client',
'destination_project/%s' % project.getRelativeUrl()
]
)
public_server_software = self.generateNewSoftwareReleaseUrl()
public_instance_type = 'public type'
software_product, release_variation, type_variation = self.addSoftwareProduct(
"instance product", project, public_server_software, public_instance_type
)
self.logout()
self.login(sale_person.getUserId())
sale_supply = self.portal.sale_supply_module.newContent(
portal_type="Sale Supply",
title="price for %s" % project.getRelativeUrl(),
source_project_value=project,
price_currency_value=currency
)
sale_supply.newContent(
portal_type="Sale Supply Line",
base_price=9,
resource_value=software_product
)
sale_supply.newContent(
portal_type="Sale Supply Line",
base_price=99,
resource="service_module/slapos_compute_node_subscription"
)
sale_supply.validate()
self.tic()
# some preparation
self.logout()
# lets join as slapos administrator, which will own few compute_nodes
owner_reference = 'owner-%s' % self.generateNewId()
self.joinSlapOS(self.web_site, owner_reference)
self.login()
owner_person = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=owner_reference).getParentValue()
# first slapos administrator assignment can only be created by
# the erp5 manager
self.addProjectProductionManagerAssignment(owner_person, project)
self.tic()
# hooray, now it is time to create compute_nodes
self.login(owner_person.getUserId())
public_server_title = 'Public Server for %s' % owner_reference
public_server_id = self.requestComputeNode(public_server_title, project.getReference())
public_server = self.portal.portal_catalog.getResultValue(
portal_type='Compute Node', reference=public_server_id)
self.setAccessToMemcached(public_server)
self.assertNotEqual(None, public_server)
self.setServerOpenPublic(public_server)
public_server.generateCertificate()
self.addAllocationSupply("for compute node", public_server, software_product,
release_variation, type_variation)
# and install some software on them
self.supplySoftware(public_server, public_server_software)
# format the compute_nodes
self.formatComputeNode(public_server)
self.logout()
self.login(project_owner_person.getUserId())
# Pay deposit to validate virtual master + one computer
deposit_amount = 42.0 + 99.0
ledger = self.portal.portal_categories.ledger.automated
outstanding_amount_list = project_owner_person.Entity_getOutstandingDepositAmountList(
currency.getUid(), ledger_uid=ledger.getUid())
amount = sum([i.total_price for i in outstanding_amount_list])
self.assertEqual(amount, deposit_amount)
# Ensure to pay from the website
outstanding_amount = self.web_site.restrictedTraverse(outstanding_amount_list[0].getRelativeUrl())
outstanding_amount.Base_createExternalPaymentTransactionFromOutstandingAmountAndRedirect()
self.tic()
self.logout()
self.login()
payment_transaction = self.portal.portal_catalog.getResultValue(
portal_type="Payment Transaction",
destination_section_uid=project_owner_person.getUid(),
simulation_state="started"
)
self.assertEqual(payment_transaction.getSpecialiseValue().getTradeConditionType(), "deposit")
# payzen/wechat or accountant will only stop the payment
payment_transaction.stop()
self.tic()
assert payment_transaction.receivable.getGroupingReference(None) is not None
self.login(project_owner_person.getUserId())
amount = sum([i.total_price for i in project_owner_person.Entity_getOutstandingDepositAmountList(
currency.getUid(), ledger_uid=ledger.getUid())])
self.assertEqual(0, amount)
self.logout()
# join as the another visitor and request software instance on public
# compute_node
self.logout()
public_reference = 'public-%s' % self.generateNewId()
self.joinSlapOS(self.web_site, public_reference)
self.login()
public_person = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference=public_reference).getParentValue()
with PinnedDateTime(self, DateTime('2024/02/17 01:01')):
public_instance_title = 'Public title %s' % self.generateNewId()
# Request a payable instance without deposit
# Change the subscription from payable to free
self.checkInstanceAllocationFromPayableToFree(public_person.getUserId(),
public_reference, public_instance_title,
public_server_software, public_instance_type,
public_server, project.getReference(),
9.0, currency, sale_person)
self.login()
public_person = self.portal.portal_catalog.getResultValue(
portal_type='ERP5 Login', reference=public_reference).getParentValue()
self.login(owner_person.getUserId())
# and the instances
self.checkInstanceUnallocation(public_person.getUserId(),
public_reference, public_instance_title,
public_server_software, public_instance_type, public_server,
project.getReference())
# and uninstall some software on them
self.logout()
self.login(owner_person.getUserId())
self.supplySoftware(public_server, public_server_software,
state='destroyed')
self.logout()
# Uninstall from compute_node
self.login()
self.simulateSlapgridSR(public_server)
self.tic()
# Check stock
inventory_list = self.portal.portal_simulation.getCurrentInventoryList(**{
'group_by_section': False,
'group_by_node': True,
'group_by_variation': True,
'resource_uid': software_product.getUid(),
'node_uid': public_person.getUid(),
'project_uid': None,
'ledger_uid': self.portal.portal_categories.ledger.automated.getUid()
})
assert len(inventory_list) == 1, len(inventory_list)
assert inventory_list[0].quantity == 1, inventory_list[0].quantity
resource_vcl = [
# 'software_release/%s' % release_variation.getRelativeUrl(),
'software_type/%s' % type_variation.getRelativeUrl()
]
resource_vcl.sort()
assert inventory_list[0].getVariationCategoryList() == resource_vcl, "%s %s" % (resource_vcl, inventory_list[0].getVariationCategoryList())
# Check accounting
transaction_list = self.portal.account_module.receivable.Account_getAccountingTransactionList(mirror_section_uid=public_person.getUid())
assert len(transaction_list) == 0, len(transaction_list)
self.login()
# Ensure no unexpected object has been created
# 2 accounting transaction / line
# 3 allocation supply / line / cell
# 1 compute node
# 2 credential request
# 2 event
# 1 instance tree
# 6 open sale order / line
# 5 (can reduce to 2) assignment
# 10 simulation mvt
# 3 packing list / line
# 3 sale supply / line
# 3 sale trade condition
# 1 software installation
# 1 software instance
# 1 software product
# 1 subscription change request
# 4 subscription requests
self.assertRelatedObjectCount(project, 49)
self.checkERP5StateBeforeExit()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>change_slapos_submitted_price</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>60.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Change Submitted Price</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/SubscriptionRequest_viewChangeSlaposSubmittedPriceDialog</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: portal.Base_checkPermission(\'subscription_change_request_module\', \'Add portal content\') and (context.getSimulationState() == \'submitted\')</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
portal = context.getPortalObject()
Base_translateString = portal.Base_translateString
subscription_request = context
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
batch = (dialog_id is None)
trade_condition_type = 'manual'
if activate_kw is None:
activate_kw = {}
if not 'tag' in activate_kw:
activate_kw['tag'] = script.id
if not (subscription_request.hasSourceSection() and (0 < subscription_request.getPrice(0))):
keep_items = {
'portal_status_level': 'warning',
'portal_status_message': Base_translateString('The Subscription Request is not a payable one')
}
if batch:
return subscription_request
return context.Base_renderForm(dialog_id, keep_items=keep_items)
# Create the Subscription Change Request
item = subscription_request.getAggregateValue()
if item.getPortalType() == 'Project':
project = item
else:
project = subscription_request.getSourceProjectValue()
customer = subscription_request.getDestinationValue()
try:
subscription_change_request = subscription_request.getResourceValue().Resource_createSubscriptionRequest(
customer,
# [software_type, software_release],
subscription_request.getVariationCategoryList(),
project,
currency_value=subscription_request.getPriceCurrencyValue(),
temp_object=True,
item_value=item,
causality_value=subscription_request,# XXX .getCausalityValue(),
portal_type='Subscription Change Request',
trade_condition_type=trade_condition_type
)
except AssertionError:
current_trade_condition = None
else:
current_trade_condition = subscription_change_request.getSpecialiseValue()
new_sale_trade_condition = None
if (current_trade_condition is None):
# Create a dedicated trade condition for the customer
# to define free price
previous_title = subscription_request.getTitle()
specialise_trade_condition = subscription_request.getSpecialiseValue()
while (specialise_trade_condition.hasSourceSection()) or (specialise_trade_condition.hasDestination()):
previous_title = specialise_trade_condition.getTitle()
specialise_trade_condition = specialise_trade_condition.getSpecialiseValue()
new_sale_trade_condition = portal.sale_trade_condition_module.newContent(
portal_type='Sale Trade Condition',
specialise_value=specialise_trade_condition,
title='%s for %s' % (previous_title, customer.getTitle()),
destination_value=customer,
destination_section_value=customer,
source_project=subscription_request.getSourceProject(),
source=subscription_request.getSource(),
price_currency=subscription_request.getPriceCurrency(),
trade_condition_type=trade_condition_type,
activate_kw=activate_kw
)
new_sale_trade_condition.validate()
subscription_request.activate(after_tag=activate_kw['tag']).SubscriptionRequest_changeFromPayableToFree(None)
keep_items = {
'portal_status_message': Base_translateString('Creating a free dedicated Trade Condition for the customer')
}
if batch:
return new_sale_trade_condition
return new_sale_trade_condition.Base_redirect(keep_items=keep_items)
elif (current_trade_condition.getDestination() == customer.getRelativeUrl()) and \
(current_trade_condition.getSourceSection(None) is None):
# There is already a manual trade condition
# without any source section (free)
new_sale_trade_condition = current_trade_condition
subscription_change_request = subscription_request.getResourceValue().Resource_createSubscriptionRequest(
customer,
# [software_type, software_release],
subscription_request.getVariationCategoryList(),
project,
currency_value=subscription_request.getPriceCurrencyValue(),
item_value=item,
causality_value=subscription_request,
portal_type='Subscription Change Request',
trade_condition_type=trade_condition_type
)
keep_items = {
'portal_status_message': Base_translateString('Creating a Subscription Change Request')
}
if batch:
return subscription_change_request
return subscription_change_request.Base_redirect(keep_items=keep_items)
else:
keep_items = {
'portal_status_level': 'error',
'portal_status_message': Base_translateString('This customer already has an incompatible dedicated Trade Condition')
}
if batch:
raise ValueError(keep_items['portal_status_message'])
return current_trade_condition.Base_redirect(keep_items=keep_items)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>dialog_id, activate_kw=None, REQUEST=None, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SubscriptionRequest_changeFromPayableToFree</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from DateTime import DateTime
portal = context.getPortalObject()
Base_translateString = portal.Base_translateString
subscription_request = context
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
batch = (dialog_id is None)
if activate_kw is None:
activate_kw = {}
tag = activate_kw.get('tag', script.id)
activate_kw['tag'] = tag
######################################################
# Prevent some mistakes
if subscription_request.getSimulationState() != 'submitted':
keep_items = {
'portal_status_level': 'warning',
'portal_status_message': Base_translateString('Can only change submitted subscription request')
}
if batch:
raise ValueError(keep_items['portal_status_message'])
return subscription_request.Base_redirect(keep_items=keep_items)
current_price = subscription_request.getPrice()
if not current_price:
keep_items = {
'portal_status_level': 'warning',
'portal_status_message': Base_translateString('Can not make a free subscription to a payable one')
}
if batch:
raise ValueError(keep_items['portal_status_message'])
return subscription_request.Base_redirect(keep_items=keep_items)
######################################################
# From payable to free
if not price:
return subscription_request.SubscriptionRequest_changeFromPayableToFree(dialog_id)
######################################################
# Change price
portal_type = 'Subscription Change Request'
subscription_change_request = portal.getDefaultModule(portal_type).newContent(
portal_type=portal_type,
causality_value=subscription_request,
start_date=DateTime(),
price=price,
# Copy all other properties
destination=subscription_request.getDestination(),
destination_section=subscription_request.getDestinationSection(),
destination_decision=subscription_request.getDestinationDecision(),
destination_project=subscription_request.getDestinationProject(),
resource=subscription_request.getResource(),
aggregate=subscription_request.getAggregate(),
variation_category_list=subscription_request.getVariationCategoryList(),
quantity_unit=subscription_request.getQuantityUnit(),
quantity=subscription_request.getQuantity(),
ledger=subscription_request.getLedger(),
source=subscription_request.getSource(),
source_section=subscription_request.getSourceSection(),
source_project=subscription_request.getSourceProject(),
price_currency=subscription_request.getPriceCurrency(),
specialise=subscription_request.getSpecialise(),
)
subscription_change_request.submit()
keep_items = {
'portal_status_message': Base_translateString('Subscription Change Request created')
}
if batch:
return subscription_change_request
return subscription_change_request.Base_redirect(keep_items=keep_items)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>price, dialog_id, activate_kw=None, REQUEST=None, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SubscriptionRequest_changeSlaposSubmittedPrice</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>SubscriptionRequest_changeSlaposSubmittedPrice</string> </value>
</item>
<item>
<key> <string>action_title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>your_price</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SubscriptionRequest_viewChangeSlaposSubmittedPriceDialog</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>SubscriptionRequest_viewChangeSlaposSubmittedPriceDialog</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_dialog</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Change Submitted Price</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="FloatField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>your_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>
<item>
<key> <string>not_float</string> </key>
<value> <string>You did not enter a floating point number.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>input_style</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>precision</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>autocomplete</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>input_style</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>input_type</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>precision</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string>figure</string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>A typical price of a unit.</string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>input_style</string> </key>
<value> <string>-1 234.5</string> </value>
</item>
<item>
<key> <string>precision</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Price</string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>here/getPrice</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: preferences.getPreference(\'preferred_money_quantity_field_width\', 10)</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: preferences.getPreference(\'preferred_money_quantity_style\', \'-1 234.5\')</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>here/getPricePrecision</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -96,6 +96,7 @@ Software Product | slapos_panel_view
Software Release Module | slapos_panel_view
Software Release | slapos_panel_view
Subscription Request Module | slapos_panel_view
Subscription Request | change_slapos_submitted_price
Subscription Request | jump_pay_my_slapos_sale_invoice_transaction
Subscription Request | slapos_panel_view
Support Request Module | slapos_panel_view
......
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