Hello all,
I have an issue while using an embedded view (numeric entry field) in a table, for a few different cells. If I only change one value (Quantity), it's happy, but if I change two values (Quantity and Price), then I get errors.
I am building on Danila Talbot's ( @danieltalbot ) scripting for a table, data transform and embedded views.
Data comes in from a named query, gets passed to a function on the table: SetColumns, then transformed for the table to display.
SetColumns code, Parameter: value (dataset):
true = True
false = False
try:
columns = system.dataset.getColumnHeaders(value)
columns.append('ExtCost')
except:
columns = value[0].keys
# Custom column order
column_order = ['PartNumber',
'Description',
'Quantity',
'Price',
'ExtCost',
'UOM', 'ContainerID',
'LeadTime',
'ReqID',
'ItemID']
output = []
container_ds = system.db.runNamedQuery("Purchasing/Dropdowns/Container_CBO")
uom_options = system.db.runNamedQuery("Inventory/Dropdowns/UOM_CBO")
for col in columns:
colObject = {"field": col, "visible": true, "editable": false, "render": "auto", "justify": "auto", "align": "center", "resizable": true,
"sortable": false, "sort": "none", "filter": { "enabled": false, "visible": "on-hover", "string": { "condition": "", "value": ""},
"number": {"condition": "", "value": ""},
"boolean": {"condition": ""},
"date": {"condition": "", "value": ""} },
"viewPath": "", "viewParams": {}, "boolean": "checkbox", "number": "value",
"progressBar": {"max": 100, "min": 0, "bar": {"color": "", "style": {"classes": ""} },
"track": {"color": "", "style": {"classes": ""} }, "value": {"enabled": true, "format": "0,0.##", "justify": "center",
"style": {"classes": ""}}}, "toggleSwitch": {"color": {"selected": "", "unselected": ""}},
"nullFormat": {"includeNullStrings": false, "strict": false, "nullFormatValue": ""},
"numberFormat": "0,0.##", "dateFormat": "MM/DD/YYYY", "width": "", "strictWidth": false,
"style": {"classes": ""},
"header": {"title": "", "justify": "center", "align": "center", "style": {"classes": ""}},
"footer": {"title": "", "justify": "left", "align": "center", "style": {"classes": ""}}}
if col == 'ReqID':
colObject['visible'] = false
elif col == 'ItemID':
colObject['visible'] = false
elif col == 'Description':
colObject['visible'] = true
colObject['width'] = 300
colObject['render'] = 'view'
colObject['viewPath'] = 'Purchasing/Objects/Txt Line Item'
colObject['viewParams']['source'] = self.custom.table_id
elif col == 'PartNumber':
colObject['visible'] = true
colObject['width'] = 160
colObject['header']['title'] = 'Part #'
colObject['render'] = 'view'
colObject['viewPath'] = 'Purchasing/Objects/Txt Line Item'
colObject['viewParams']['source'] = self.custom.table_id
elif col == 'Quantity':
colObject['visible'] = true
colObject['width'] = 75
colObject['render'] = 'view'
colObject['viewPath'] = 'Purchasing/Objects/Num Line Item'
colObject['viewParams']['source'] = self.custom.table_id
elif col == 'Price':
colObject['visible'] = true
colObject['width'] = 75
colObject['render'] = 'view'
colObject['viewPath'] = 'Purchasing/Objects/Num Line Item'
colObject['viewParams']['source'] = self.custom.table_id
elif col == 'ExtCost':
colObject['visible'] = true
colObject['width'] = 75
colObject['render'] = 'view'
colObject['viewPath'] = 'Purchasing/Objects/Num Line Item'
colObject['viewParams']['source'] = self.custom.table_id
elif col == 'LeadTime':
colObject['visible'] = true
colObject['width'] = 80
colObject['header']['title'] = 'Lead Time'
colObject['render'] = 'view'
colObject['viewPath'] = 'Purchasing/Objects/Num Line Item'
colObject['viewParams']['source'] = self.custom.table_id
elif col == 'UOM':
colObject['visible'] = true
colObject['width'] = 80
colObject['render'] = 'view'
colObject['viewPath'] = 'Purchasing/Objects/Drop Line Items'
colObject['viewParams']['source'] = self.custom.table_id
colObject['viewParams']['options'] = uom_options
colObject['viewParams']['placeHolder'] = 'UOM...'
elif col == 'ContainerID':
colObject['visible'] = true
colObject['width'] = 100
colObject['render'] = 'view'
colObject['viewPath'] = 'Purchasing/Objects/Drop Line Items'
colObject['viewParams']['source'] = self.custom.table_id
colObject['viewParams']['options'] = container_ds
colObject['viewParams']['placeHolder'] = 'Container...'
output.append(colObject)
output_sorted = []
for col_name in column_order:
for col_obj in output:
if col_obj['field'] == col_name:
output_sorted.append(col_obj)
break
self.props.columns = output_sorted
return output_sorted
The script transform code on table.custom.data
, to which the table.props.data
is bound:
columns = self.SetColumns(value) # value is a dataset
output = []
# for each row in the dataset
for row in range(value.getRowCount()):
row_value = {'updateMe':False, 'deleteMe':False}
price = 0
qty = 0
# for each column in the column list:
for col in columns:
colName = col['field']
if colName == 'Price':
price = value.getValueAt(row, colName)
if colName == 'Quantity':
qty = value.getValueAt(row, colName)
if colName != 'ExtCost':
cellValue = value.getValueAt(row, colName)
row_value[colName] = {'value':cellValue, 'action':'update'
, 'enabled':True, 'display':True, 'style':{}}
ext_cost = round(price * qty, 2)
row_value['ExtCost'] = {
'value': ext_cost,
'action':'update',
'enabled':False,
'display':True,
'style':{}
}
output.append(row_value)
return output
Embedded view parameters:
column, rowData{}, rowIndex, source (table identifier), value
custom.props
:
the custom.value
elements are expression bound like this, for each one:
try({view.params.value}['action'],{view.params.value})
the custom.payload
elements are property bound like this:
view.params.rowData
newValue
is bound to the Numberic Entry Field's props.value
.
The props.value
is expression bound like so:
coalesce({view.custom.value.value}, 0)
There is an onChange
script on the props.value
:
if origin != 'Binding':
if currentValue.value != previousValue.value:
if self.view.params.source == 'lineItems':
message = 'recalcExtCost'
payload = self.view.custom.payload
scope = 'page'
system.perspective.sendMessage(message, payload, scope)
And finally the message handler code that throws the error:
rowIndex = int(payload['rowIndex'])
column = payload['column']
newValue = payload['newValue']
# message = str(rowIndex) + ': ' + str(column) + ': ' + str(newValue)
system.perspective.print(column)
self.custom.table_data[rowIndex][column] = newValue
if column == 'Quantity':
price = self.custom.table_data[rowIndex].get('Price',0)
quantity = newValue
elif column == 'Price':
price = newValue
quantity = self.custom.table_data[rowIndex].get('Quantity',0)
# quantity = self.custom.table_data[rowIndex]['Quantity']['value']
system.perspective.print(price)
system.perspective.print(quantity)
ext_cost = round(price * quantity, 2)
self.custom.table_data[rowIndex]['ExtCost'] = ext_cost
And the error message:
Error running Purchasing/Primary Views/Request Form@D/root/Flx Bottom/Flx Bottom Left/Flx Bottom Table/Table.onMessageReceived(self, payload): Traceback (most recent call last): File "<function:onMessageReceived>", line 20, in onMessageReceived TypeError: unsupported operand type(s) for *: 'com.inductiveautomation.perspective.gateway.script.PropertyTreeScriptWrapper$ObjectWrapper' and 'long'
The error message changes a little depending on how I try to extract the actual value, but it all seems to relate to not being able to get just the numbers from both embedded views and multiplying them. I have been using Copilot to help me figure this out, but it's recommending something I don't understand on changing the embedded view's props.value
expression binding, which caused some other error, and now it's recommending I do something completely different.
Is there a simple fix I can use in the message handler or something else I should do?
Thank you!