Implement the OnNotify method in the PS_PT application package, in the Integration sub-package, to handle inbound asynchronous transactions. All the examples in this section are assumed to be implementations of the OnNotify method.
Message Class Inbound Asynchronous Example 1
The following example implements the OnNotify method.
import PS_PT:Integration:INotificationHandler;
class FLIGHTPROFILE implements PS_PT:Integration:INotificationHandler
method FLIGHTPROFILE();
method OnNotify(&_MSG As Message);
end-class;
/* constructor */
method FLIGHTPROFILE
end-method;
method OnNotify
/+ &_MSG as Message +/
/+ Extends/implements PS_PT:Integration:INotificationHandler.+/
/+ OnNotify +/
/* Variable Declaration */
Local any &outstring;
Local any &i;
Local any &CRLF;
Local Message &MSG;
Local Rowset &rs, &rs1;
Local Record &FLIGHTDATA, &REC;
Local string &acnumber_value, &msi_sensor_value, &ofp_value,
&actype_value, &callsign_value, &squadron_value, &comm1_value,
&comm2_value, &ecm_value;
Local XmlDoc &xmldoc;
Local string &return_string_value;
Local boolean &return_bool_value;
&CRLF = Char(13) | Char(10);
&MSG = &_MSG;
&rs = &MSG.GetRowset();
&REC = &rs(1).QE_FLIGHTDATA;
&FLIGHTDATA = CreateRecord(Record.QE_FLIGHTDATA);
&REC.CopyFieldsTo(&FLIGHTDATA);
/* Parse out Message Data */
&acnumber_value = &FLIGHTDATA.QE_ACNUMBER.Value;
&msi_sensor_value = &FLIGHTDATA.QE_MSI_SENSOR.Value;
&ofp_value = &FLIGHTDATA.QE_OFP.Value;
&actype_value = &FLIGHTDATA.QE_ACTYPE.Value;
&callsign_value = &FLIGHTDATA.QE_CALLSIGN.Value;
&squadron_value = &FLIGHTDATA.QE_SQUADRON.Value;
&comm1_value = &FLIGHTDATA.QE_COMM1.Value;
&comm2_value = &FLIGHTDATA.QE_COMM2.Value;
&ecm_value = &FLIGHTDATA.QE_ECM.Value;
&outstring = "Send Async FLight test";
/* Construct Output String */
&outstring = &outstring | &acnumber_value | &CRLF |
&msi_sensor_value |
&CRLF | &ofp_value | &CRLF | &actype_value | &CRLF |
&callsign_value |
&CRLF | &squadron_value | &CRLF | &comm1_value | &CRLF |
&comm2_value |
&CRLF | &ecm_value;
/* Log Output String into page record */
&FLIGHTDATA.GetField(Field.DESCRLONG).Value = &outstring;
SQLExec("DELETE FROM PS_QE_FLIGHTDATA");
&FLIGHTDATA.Insert();
end-method;
Message Class Inbound Asynchronous Example 2
The following example shows notification PeopleCode that checks the PSCAMA to determine the audit action code and that examines the language code to determine whether related language processing is needed:
method OnNotify
/+ &MSG as Message +/
/* Simple PeopleCode Notifcation- - Check the PSCAMA*/
Local Rowset &MSG_RS;
Local Record &REC_NAME_PREFIX, &REC, &REC_RL;
Local integer &I;
Local string &ACTION, &LNG, &BASE_LNG, &RELLNG, &KEY1, &KEY2;
&MSG_RS = &MSG.GetRowset();
For &I = 1 To &MSG_RS.RowCount /* Loop through all transactions */
&REC = &MSG_RS.GetRow(&I).GetRecord(Record.NAME_PREFIX_TBL);
/* Get Audit Action for this transaction */
&ACTION = &MSG_RS.GetRow(&I).PSCAMA.AUDIT_ACTN.Value;
/* Get Language code for this transaction */
&LNG = &MSG_RS.GetRow(&I).PSCAMA.LANGUAGE_CD.Value;
&BASE_LNG = %Language;
Evaluate &ACTION /*****************************/
/******** Add a Record *******/
/*****************************/
When "A"
/* Add the base language record */
&REC_NAME_PREFIX = CreateRecord(Record.NAME_PREFIX_TBL);
&REC.CopyFieldsTo(&REC_NAME_PREFIX);
&REC_NAME_PREFIX.ExecuteEdits();
If &REC_NAME_PREFIX.IsEditError Then
/* error handling */
Else
&REC_NAME_PREFIX.Insert();
/* Need error handling here if insert fails */
If &LNG <> %Language Then
/* add related language record */
&RELLNG = &REC.RelLangRecName;
&REC_RL = CreateRecord(Record.NAME_PREFIX_LNG);
&REC.CopyFieldsTo(&REC_RL);
&REC_RL.LANGUAGE_CD.Value = &LNG;
&REC_RL.Insert();
/* Need error handling here if insert fails */
End-If;
End-If;
/*****************************/
/***** Change a Record *******/
/*****************************/
/**** Using record objects ***/
When "C"
/* Get the Record - insert it */
&KEY1 = &REC.GetField(Field.NAME_PREFIX).Value;
&REC_NAME_PREFIX = CreateRecord(Record.NAME_PREFIX_TBL);
&REC_NAME_PREFIX.NAME_PREFIX.Value = &REC.GetField(Field.
NAME_PREFIX).Value;
If &REC_NAME_PREFIX.SelectByKey() Then
&REC.CopyFieldsTo(&REC_NAME_PREFIX);
&REC_NAME_PREFIX.ExecuteEdits();
If &REC_NAME_PREFIX.IsEditError Then
/* error handling */
Else
&REC_NAME_PREFIX.Update();
End-If;
Else
&REC.CopyFieldsTo(&REC_NAME_PREFIX);
&REC_NAME_PREFIX.ExecuteEdits();
If &REC_NAME_PREFIX.IsEditError Then
/* error handling */
Else
&REC_NAME_PREFIX.Insert();
End-If;
End-If;
/*****************************/
/****** Delete a Record ******/
/*****************************/
/*** Using SQLExec ***********/
When "D"
/* Get the Record using SQLExec- error */
&KEY1 = &REC.GetField(Field.NAME_PREFIX).Value;
SQLExec("Select NAME_PREFIX from PS_NAME_PREFIX_TBL where
NAME_PREFIX = :⇒1", &KEY1, &KEY2);
If None(&KEY2) Then
/* Send to error log */
Else
SQLExec("Delete from PS_NAME_PREFIX_TBL where
NAME_PREFIX = :1", &KEY1);
SQLExec("Delete from PS_NAME_PREFIX_LNG where
NAME_PREFIX = :1", &KEY1);
End-If;
End-Evaluate;
End-For;
end-method;
Message Class Inbound Asynchronous Example 3
There’s a practical limit to how large a message can be, and this can be controlled by a system-wide variable called %MaxMessageSize. The system administrator can change this size in the PSOPTIONS settings. This size can be set only for all messages, not for individual definitions.
PeopleCode that populates a Message object should include code that is similar to the following example to check the message size before inserting a new Level 0 row.
Note: Always code the %MaxMessageSize checkpoint at the Level 0 record. A batch of transactions can be split across multiple messages, but do not split a single transaction (logical unit of work) into multiple messages.
Local SQL &hdr_sql, &ln_sql;
Local Message &MSG;
Local Rowset &hdr_rs, &ln_rs;
Local Record &hdr_rec, &ln_rec, &hdr_rec_msg, &ln_rec_msg;
/* This PeopleCode will publish messages for a simple Header/
Line record combination. Multiple Header/Lines are copied to the
message until the %MaxMessageSize is exceeded at which point a
new message is built. This references MSR_HDR_INV (Header) and
DEMAND_INF_INV (Line) records */
/* Create an instance of the STOCK_REQUEST message */
&MSG = CreateMessage(OPERATION.STOCK_REQUEST);
/* Create an App. Message Rowset that includes the
MSR_HDR_INV (Header) and DEMAND_INF_INV (Line)*/
&hdr_rs = &MSG.GetRowset();
/* Create a SQL object to select the Header rows */
&hdr_sql = CreateSQL("Select * from PS_MSR_HDR_INV
WHERE BUSINESS_UNIT='M04A1'
AND ORDER_NO LIKE 'Z%' ORDER BY BUSINESS_UNIT,ORDER_NO");
&I = 1;
/* Create record objects for the Header and Lines */
&ln_rec = CreateRecord(Record.DEMAND_INF_INV);
&hdr_rec = CreateRecord(Record.MSR_HDR_INV);
/* Loop through each Header row that is fetched */
While &hdr_sql.Fetch(&hdr_rec)
/* Publish the message if its size exceeds the MaxMessageSize
/* specified in Utilities/Use/PeopleTools Options */
If &MSG.Size > %MaxMessageSize Then
%IntBroker.Publish(&MSG);
&I = 1;
/* Create a new instance of the message object */
&MSG = CreateMessage(OPERATION.STOCK_REQUEST);
&hdr_rs = &MSG.GetRowset();
End-If;
If &I > 1 Then
&hdr_rs.InsertRow(&I - 1);
End-If;
/* Instantiate the row within the Header portion of the
App Message rowset to which data will be copied */
&hdr_rec_msg = &hdr_rs.GetRow(&I).GetRecord(Record.MSR_HDR_INV);
/* Copy data into the level 0 (Header portion) of
/* &MSG message structure */
&hdr_rec.CopyFieldsTo(&hdr_rec_msg);
/* Publish the last message if it has been changed*/
If &hdr_rec_msg.IsChanged Then
%IntBroker.Publish(&MSG);
End-If;
End-While;
Message Class Inbound Asynchronous Example 4
The following code example shows how to get data out of the IBInfo object for a rowset-based message.
Local Rowset &rs, &rs1;
Local Record &FLIGHTDATA, &REC;
Local string &acnumber_value, &msi_sensor_value, &ofp_value,
&actype_value, &callsign_value, &squadron_value, &comm1_value,
&comm2_value, &ecm_value, &datetime;
Local XmlDoc &xmldoc;
Local string &data;
Local boolean &return_bool_value;
&CRLF = Char(13) | Char(10);
/* this is how one would access data from IBinfo and
/* IBConnectorInfo objects*/
&return_bool_value = &MSG.IBInfo.ConnectorOverride;
For &i = 1 To &MSG.IBInfo.IBConnectorInfo.GetNumberOfConnector
Properties()
&data = &MSG.IBInfo.IBConnectorInfo.GetQueryStringArgName(&i);
&data = &MSG.IBInfo.IBConnectorInfo.GetQueryStringArgValue(&i);
End-For;
&MSG.IBInfo.IBConnectorInfo.ClearConnectorProperties();
&data = &MSG.IBInfo.IBConnectorInfo.ConnectorName;
&data = &MSG.IBInfo.IBConnectorInfo.ConnectorClassName;
&data = &MSG.IBInfo.IBConnectorInfo.RemoteFrameworkURL;
&data = &MSG.IBInfo.IBConnectorInfo.PathInfo;
&data = &MSG.IBInfo.IBConnectorInfo.Cookies;
For &i = 1 To &MSG.IBInfo.IBConnectorInfo.GetNumberOfQueryStringArgs()
&data = &MSG.IBInfo.IBConnectorInfo.GetConnectorPropertiesName(&i);
&data = &MSG.IBInfo.IBConnectorInfo.GetConnectorPropertiesValue
(&i);
&data = &MSG.IBInfo.IBConnectorInfo.GetConnectorPropertiesType(&i);
End-For;
&MSG.IBInfo.IBConnectorInfo.ClearQueryStringArgs();
&data = &MSG.IBInfo.MessageType;
&data = &MSG.IBInfo.RequestingNodeName;
&data = &MSG.IBInfo.OrigUser;
&data = &MSG.IBInfo.OrigNode;
&data = &MSG.IBInfo.AppServerDomain;
&data = &MSG.IBInfo.OrigProcess;
&data = &MSG.IBInfo.OrigTimeStamp;
&data = &MSG.IBInfo.DestinationNode;
&data = &MSG.IBInfo.FinalDestinationNode;
&data = &MSG.IBInfo.SourceNode;
&data = &MSG.IBInfo.MessageName;
&data = &MSG.IBInfo.MessageVersion;
&data = &MSG.IBInfo.VisitedNodes;
/* get the content data from the message rowset*/
&rs = &MSG.GetRowset();
&REC = &rs(1).QE_FLIGHTDATA;
&FLIGHTDATA = CreateRecord(Record.QE_FLIGHTDATA);
&REC.CopyFieldsTo(&FLIGHTDATA);
/* Parse out Message Data */
&acnumber_value = &FLIGHTDATA.QE_ACNUMBER.Value;
&msi_sensor_value = &FLIGHTDATA.QE_MSI_SENSOR.Value;
&ofp_value = &FLIGHTDATA.QE_OFP.Value;
&actype_value = &FLIGHTDATA.QE_ACTYPE.Value;
&callsign_value = &FLIGHTDATA.QE_CALLSIGN.Value;
&squadron_value = &FLIGHTDATA.QE_SQUADRON.Value;
&comm1_value = &FLIGHTDATA.QE_COMM1.Value;
&comm2_value = &FLIGHTDATA.QE_COMM2.Value;
&ecm_value = &FLIGHTDATA.QE_ECM.Value;
&datetime = &FLIGHTDATA.ACTIONDTTM.Value;
&outstring = "Send Async FLight test";
/* Construct Output String */
&outstring = &outstring | &acnumber_value | &CRLF | &msi_sensor_value
| &CRLF | &ofp_value | &CRLF | &actype_value | &CRLF
| &callsign_value | &CRLF | &squadron_value | &CRLF
| &comm1_value | &CRLF | &comm2_value | &CRLF | &ecm_value |
&datetime;
/* Log Output String into page record */
&FLIGHTDATA.GetField(Field.DESCRLONG).Value = &outstring;
SQLExec("DELETE FROM PS_QE_FLIGHTDATA");
&FLIGHTDATA.Insert();
Message Class Inbound Asynchronous Example 5
The following code example shows how to get data out of the IBInfo object for a nonrowset-based message.
Local XmlDoc &xmldoc;
Local XmlNode &node, &root, &acct_id_node, &acct_name_node,
&address_node, &phone_node;
Local string &outstring, &CRLF;
Local Record &FLIGHT_DATA_INFO, &REC;
Local string &data;
Local boolean &return_bool_value;
/* this is how one wouild access data from IBinfo and
/* IBConnectorInfo objects*/
&return_bool_value = &MSG.IBInfo.ConnectorOverride;
For &i = 1 To &MSG.IBInfo.IBConnectorInfo.GetNumberOfConnector
Properties()
&data = &MSG.IBInfo.IBConnectorInfo.GetQueryStringArgName(&i);
&data = &MSG.IBInfo.IBConnectorInfo.GetQueryStringArgValue(&i);
End-For;
&MSG.IBInfo.IBConnectorInfo.ClearConnectorProperties();
&data = &MSG.IBInfo.IBConnectorInfo.ConnectorName;
&data = &MSG.IBInfo.IBConnectorInfo.ConnectorClassName;
&data = &MSG.IBInfo.IBConnectorInfo.RemoteFrameworkURL;
&data = &MSG.IBInfo.IBConnectorInfo.PathInfo;
&data = &MSG.IBInfo.IBConnectorInfo.Cookies;
For &i = 1 To &MSG.IBInfo.IBConnectorInfo.GetNumberOfQueryStringArgs()
&data = &MSG.IBInfo.IBConnectorInfo.GetConnectorPropertiesName(&i);
&data = &MSG.IBInfo.IBConnectorInfo.GetConnectorPropertiesValue
(&i);
&data = &MSG.IBInfo.IBConnectorInfo.GetConnectorPropertiesType(&i);
End-For;
&MSG.IBInfo.IBConnectorInfo.ClearQueryStringArgs();
&data = &MSG.IBInfo.MessageType;
&data = &MSG.IBInfo.RequestingNodeName;
&data = &MSG.IBInfo.OrigUser;
&data = &MSG.IBInfo.OrigNode;
&data = &MSG.IBInfo.AppServerDomain;
&data = &MSG.IBInfo.OrigProcess;
&data = &MSG.IBInfo.OrigTimeStamp;
&data = &MSG.IBInfo.DestinationNode;
&data = &MSG.IBInfo.FinalDestinationNode;
&data = &MSG.IBInfo.SourceNode;
&data = &MSG.IBInfo.MessageName;
&data = &MSG.IBInfo.MessageVersion;
&data = &MSG.IBInfo.VisitedNodes;
&xmldoc = &MSG.GetXmlDoc();
&CRLF = Char(13) | Char(10);
&root = &xmldoc.DocumentElement;
/* Get values out of XMLDoc */
&node_array = &root.GetElementsByTagName("actype");
&ac_type_node = &node_array.Get(1);
&ac_type_value = &ac_type_node.NodeValue;
&node_array = &root.GetElementsByTagName("msi_sensor");
&msi_sensor_node = &node_array.Get(1);
&msi_sensor_value = &msi_sensor_node.NodeValue;
&node_array = &root.GetElementsByTagName("callsign");
&callsign_node = &node_array.Get(1);
&callsign_value = &callsign_node.NodeValue;
&node_array = &root.GetElementsByTagName("ofp");
&ofp_node = &node_array.Get(1);
&ofp_value = &ofp_node.NodeValue;
&outstring = "GetDataout of xmldoc Test";
&outstring = &outstring | &CRLF | &ac_type_value | &CRLF |
&msi_sensor_node
| &CRLF | &callsign_value | &CRLF | &ofp_value;
/* Write out the result string */
SQLExec("DELETE FROM PS_QE_FLIGHT_DATA");
&FLIGHT_DATA_INFO = CreateRecord(Record.QE_FLIGHT_DATA);
&FLIGHT_DATA_INFO.GetField(Field.DESCRLONG).Value = &outstring;
&FLIGHT_DATA_INFO.Insert();
Message Class Inbound Asynchronous Example 6
The following example show a notification that could be an implementation of the OnNotify method, using a component interface in the notification. This example shows error trapping and has multi language support:
Component string &PUBNODENAME;
/* save pubnodename to prevent circular publishes */
Local Message &MSG;
Local Rowset &MSG_ROWSET, &cur_rowset;
Local ApiObject &oSession;
Local ApiObject &CONTACT_CI;
Local number &I;
Local string &KEY1;
Local Record &REC;
Local boolean &BC_CREATE, &ADD;
Local boolean &TRANSACTION_ERROR, &MSG_ERROR;
/** Transaction/Message Error Flags**/
Function errorHandler()
Local ApiObject &oPSMessageColl;
Local ApiObject &oPSMessage;
Local string &strErrMsgSetNum, &strErrMsgNum, &strErrMsgText,
&strErrType;
&oPSMessageColl = &oSession.PSMessages;
For &I = 1 To &oPSMessageColl.Count
&oPSMessage = &oPSMessageColl.Item(&I);
&strErrMsgSetNum = &oPSMessage.MessageSetNumber;
&strErrMsgNum = &oPSMessage.MessageNumber;
&strErrMsgText = &oPSMessage.Text;
&LogFile.WriteLine(&strErrType | " (" | &strErrMsgSetNum | ","
| &strErrMsgNum | ") - " | &strErrMsgText);
End-For;
rem ***** Delete the Messages from the collection *****;
&oPSMessageColl.DeleteAll();
End-Function;
Function DO_CI_SUBSCRIBE()
&oSession = %Session;
&CONTACT_CI = &oSession.GETCOMPONENT(CompIntfc.CONTACT);
If (&CONTACT_CI = Null) Then
/* Replace this message with Tools message set when available */
Error MsgGet(91, 58, " Unable to get the Component Interface.");
Exit (1);
End-If;
/** Set Component Interface Properties **/
&CONTACT_CI.GetHistoryItems = True;
&CONTACT_CI.Interactivemode = False; /** set this to True while
debugging **/
rem Send messages to the PSMessage Collection;
&oSession.PSMessagesMode = 1;
&MSG_ERROR = False;
For &I = 1 To &MSG_ROWSET.ActiveRowCount
/** Set Session Language Code Property **/
®IONALSETTINGS = &oSession.RegionalSettings;
®IONALSETTINGS.LanguageCd = &MSG_ROWSET(&I).PSCAMA.
LANGUAGE_CD.Value;
&TRANSACTION_ERROR = False;
&BC_CREATE = False;
/** Instantiate Component Interface **/
&KEY1 = &MSG_ROWSET(&I).CONTACT_TBL.PERSON_ID.Value;
&CONTACT_CI.PERSON_ID = &KEY1;
Evaluate &MSG_ROWSET(&I).PSCAMA.AUDIT_ACTN.Value
When = "A"
When = "N"
&ADD = True;
/* Check if Keys already exist. */
&CONTACT_CIColl = &CONTACT_CI.Find();
/*If None(&EXISTS) Then*/
If &CONTACT_CIColl.Count = 0 Then
If &CONTACT_CI.Create() Then
&BC_CREATE = True;
Else
/* Replace this message with Tools message set
when available */
Warning MsgGet(18022, 56, "Error creating Component
Interface for transaction %1", &I);
&TRANSACTION_ERROR = True;
End-If;
Else
If Not &CONTACT_CI.Get() Then
/* Replace this message with Tools message set
when available */
Warning MsgGet(18022, 59, "Could not Get Component
Interface for transaction %1", &I);
&TRANSACTION_ERROR = True;
End-If;
End-If;
Break;
When = "C"
&ADD = False;
If Not &CONTACT_CI.Get() Then
/* Replace this message with Tools message set when
available */
Warning MsgGet(18022, 59, "Could not Get Component
Interface for transaction %1", &I);
&TRANSACTION_ERROR = True;
End-If;
Break;
When = "D"
When = "K"
When-Other
/* delete and old key action codes not allowed at this
time */
&TRANSACTION_ERROR = True;
Warning MsgGet(18022, 61, "Audit Action 'D' not allowed on
transaction %1", &TRANSACTION);
Break;
End-Evaluate;
&CONTACT_CI.CopyRowset(&MSG_ROWSET, &I);
If Not &TRANSACTION_ERROR Then
If Not &CONTACT_CI.save() Then
/* Replace this message with Tools message set when
available */
Warning MsgGet(18022, 57, "Error saving Component
Interface for transaction %1", &TRANSACTION);
&TRANSACTION_ERROR = True;
End-If;
End-If;
/** close the last Component Interface in preparation for
getting the next **/
If Not &CONTACT_CI.Cancel() Then
/* Replace this message with Tools message set when
available */
Warning MsgGet(18022, 58, "Error Canceling Component
Interface for transaction %1", &TRANSACTION);
Exit (1);
End-If;
/* Reset &TRANSACTION_ERROR to "False" for &BusComp.Save()
to execute if no
/* Transaction Error found in the next Transaction. */
&TRANSACTION_ERROR = False;
End-For;
If &TRANSACTION_ERROR Then
&MSG_ERROR = True;
End-If;
End-Function;
/**** Main Process ****/
&MSG.ExecuteEdits(%Edit_Required + %Edit_TranslateTable);
If &MSG.IsEditError Then
&MSG_ERROR = True;
Else
&PUBNODENAME = &MSG.PubNodeName;
&MSG_ROWSET = &MSG.GetRowset();
/* Do Component Interface subscribe */
DO_CI_SUBSCRIBE();
End-If;
If &MSG_ERROR Then
Exit (1);
End-If;
XmlDoc Class Inbound Asynchronous Example
The following example uses the GetXmlDoc method.
Local XmlDoc &Document;
Local XmlNode &node, &root;
Local string &outstring;
Local Rowset &LEVEL0;
Local Record &SALES_ORDER_INFO, &REC;
&CRLF = Char(13) | Char(10);
& Document = &MSG.GetXmlDoc();
&root = & Document.DocumentElement;
&child_count = &root.ChildNodeCount;
/* Get values out of XmlDoc */
&node_array = &root.GetElementsByTagName("QE_ACCT_ID");
&acct_id_node = &node_array.Get(2);
&account_id_value = &acct_id_node.NodeValue;
&node_array = &root.GetElementsByTagName("QE_ACCOUNT_NAME");
&acct_name_node = &node_array.Get(2);
&account_name_value = &acct_name_node.NodeValue;
&node_array = &root.GetElementsByTagName("QE_ADDRESS");
&address_node = &node_array.Get(2);
&address_value = &address_node.NodeValue;
&node_array = &root.GetElementsByTagName("QE_PHONE");
&phone_node = &node_array.Get(2);
&phone_value = &phone_node.NodeValue;
&outstring = "GetMessageXmlDoc Test";
&outstring = &outstring | &CRLF | &account_id_value | &CRLF
| &account_name_value | &CRLF | &address_value | &CRLF |
&phone_value;
&SALES_ORDER_INFO = CreateRecord(Record.QE_SALES_ORDER);
&SALES_ORDER_INFO.GetField(Field.QE_ACCT_ID).Value =
&account_id_value;
&SALES_ORDER_INFO.GetField(Field.DESCRLONG).Value = &outstring;
&SALES_ORDER_INFO.Update();
No comments:
Post a Comment