Hi All,
I am trying to achieve the below XML response where when we pass the Customer ID and TransDate as input, system should send the Sales ID's and the Invoice ID's which are related to the input parameters.
Service Request:
<?xml version="1.0"?>
<LoginResponse>
<CustId>1234ab5678</CustId>
<Date>10/10/2018</Date>
</LoginResponse>
Expected Service Response:
<?xml version="1.0"?>
<CustomerInfoResponse>
<CustId>1234ab5678</CustId>
<Date>10/10/2018</Date>
<SalesOrder>
<SalesId>800000XXX1</SalesId>
<SaleTransDate>10/10/2018</SaleTransDate>
<Address>123 MG Road, 219435</Address>
<Invoice>
<InvoiceNumber>914200XXX1</InvoiceNumber>
<InvoiceNumber>914200XXX2</InvoiceNumber>
<InvoiceNumber>914200XXX3</InvoiceNumber>
</Invoice>
</SalesOrder>
<SalesOrder>
<SalesId>800000XXX2</SalesId>
<SaleTransDate>10/10/2018</SaleTransDate>
<Address>456 MG Road, 219445,</Address>
<Invoice>
<InvoiceNumber>914200XXX4</InvoiceNumber>
<InvoiceNumber>914200XXX5</InvoiceNumber>
<InvoiceNumber>914200XXX6</InvoiceNumber>
</Invoice>
</SalesOrder>
<SalesOrder>
<SalesId>800000XXX3</SalesId>
<SaleTransDate>10/10/2018</SaleTransDate>
<Address>456 MG Road, 219445,</Address>
<Invoice>
<InvoiceNumber>914200XXX7</InvoiceNumber>
<InvoiceNumber>914200XXX8</InvoiceNumber>
<InvoiceNumber>914200XXX9</InvoiceNumber>
</Invoice>
</SalesOrder>
</CustomerInfoResponse>
Request you all to help me to understand, how can i send the list of both the SalesOrder and the Invoive.
*This post is locked for comments
Thanks Martin.
That's what you asked your code to do. Look at this logic in customerInfo():
salesOrderContract = new SalesOrderContract(); while select * from salesRecords { salesOrderContract.parmSalesId(salesRecords.SalesId); ... _salesorderList.addEnd(salesOrderContrat); }
You've created just a single instance of SalesOrderContract and you're changing this single instance inside the loop. Therefore you're overwriting what you set before. To fix this, you must create multiple objects - simply move the instantiation inside the loop:
while select * from salesRecords { salesOrderContract = new SalesOrderContract(); salesOrderContract.parmSalesId(salesRecords.SalesId); ... _salesorderList.addEnd(salesOrderContrat); }
Hi Martin,
As per your inputs i have changed my approach by creating two separate request for invoice and packing slip scenarios, but when am adding my values to Subcontract parm methods - the values are overridden with the new rather than storing them as list.
Service Request:
<?xml version="1.0"?>
<LoginResponse>
<CustId>1234ab5678</CustId>
<Date>10/10/2018</Date>
</LoginResponse>
Expected Response Schema:
<?xml version="1.0"?>
<CustomerInfoResponse>
<CustId>1234ab5678</CustId>
<Date>10/10/2018</Date>
<SalesOrder>
<SalesId>800000XXX1</SalesId>
<SaleTransDate>10/10/2018</SaleTransDate>
<Address>123 MG Road, 219435</Address>
<Invoice>
<InvoiceNumber>914200XXX1</InvoiceNumber>
<Amount>13434</Amount>
</Invoice>
<Invoice>
<InvoiceNumber>914200XXX2</InvoiceNumber>
<Amount>1200</Amount>
</Invoice>
<Invoice>
<InvoiceNumber>914200XXX3</InvoiceNumber>
<Amount>11453</Amount>
</Invoice>
</SalesOrder>
</CustomerInfoResponse>
Below are the classes that i have used to achieve the expected results,
InvoiceDataContract:
[
DataContractAttribute("Invoice")
]
class InvoiceContract
{
str InvoiceNumber;
AmountMST Amount;
}
[
DataMemberAttribute("InvoiceNumber")
]
public str parmInvoiceNumber(str _InvoiceNumber = InvoiceNumber)
{
InvoiceNumber = _InvoiceNumber ;
return InvoiceNumber ;
}
[
DataMemberAttribute("Amount")
]
public AmountMST parmAmount(AmountMST _Amount= Amount)
{
Amount= _Amount;
return Amount;
}
SalesOrderDataContract:
[
DataContractAttribute("SalesOrder")
]
class SalesOrderContract
{
str SalesId;
TransDate SaleTransDate;
LogisticsAddressing Address;
List Invoive;
}
[
DataMemberAttribute("SalesId")
]
public str parmSalesId(str _SalesId= SalesId)
{
SalesId= _SalesId;
return SalesId;
}
[
DataMemberAttribute("SaleTransDate")
]
public TransDate parmSaleTransDate(TransDate _SaleTransDate= SaleTransDate)
{
SaleTransDate= _SaleTransDate;
return SaleTransDate;
}
[
DataMemberAttribute("Address")
]
public LogisticsAddressing parmAddress(LogisticsAddressing _Address= Address)
{
Address= _Address;
return Address;
}
[
DataMemberAttribute("Invoice"),
AifCollectionTypeAttribute('return', Types::Class, classstr(InvoiceContract))
]
public List parmInvoice(List _Invoice= Invoice)
{
Invoice = _Invoice;
return Invoice;
}
CustomerInfoResponseContract:
[
DataContractAttribute("CustomerInfoResponse")
]
class CustomerInfoResponseContract
{
str CustId;
TransDate Date;
List SalesOrder;
}
[
DataMemberAttribute("CustId")
]
public str parmCustId(str _CustId= CustId)
{
CustId= _CustId;
return SalesId;
}
[
DataMemberAttribute("Date")
]
public TransDate parmDate(TransDate _Date= Date)
{
Date= _Date;
return Date;
}
[
DataMemberAttribute("SalesOrder"),
AifCollectionTypeAttribute('return', Types::Class, classstr(SalesOrderContract))
]
public List parmSalesOrder(List _SalesOrder= SalesOrder)
{
SalesOrder= _SalesOrder;
return SalesOrder;
}
Service Operation:
[
SysEntryPointAttribute(true),
AifCollectionTypeAttribute('return', Type::Class, classStr(CustomerInfoResponseContract))
]
public List customerInfo(str CustID, TransDate Date)
{
List _customerInfoList = new List(Types::Class);
List _salesorderList = new List(Types::Class);
List _invoiceList = new List(Types::Class);
customerInfoResponseContract = new CustomerInfoResponseContract();
salesOrderContract = new SalesOrderContract ();
invoiceContract= new InvoiceContract();
//After the filtration from Sales and Invoice tables
while select * from salesRecords
{
while select * from invoiceRecords
{
invoiceContract.parmInvoiceNumber(invoiceRecords.InvoiveId);
invoiceContract.parmAmount(invoiceRecords.Amount);
_invoiceList.addEnd(invoiceContract);
}
salesOrderContract.parmSalesId(salesRecords.SalesId);
salesOrderContract.parmSaleTransDate(salesRecord.TransDate);
salesOrderContract.parmAddress(salesRecord.Address);
salesOrderContrat.parmInvoice(_invoiceList);
_salesorderList.addEnd(salesOrderContrat);
}
customerInfoResponseContract .parmCustId(CustId);
customerInfoResponseContract .parmDate(Date);
_customerInfoList .addEnd(customerInfoResponseContract );
return _customerInfoList ;
}
Output:
<?xml version="1.0"?>
<CustomerInfoResponse>
<CustId>1234ab5678</CustId>
<Date>10/10/2018</Date>
<SalesOrder>
<SalesId>800000XXX1</SalesId>
<SaleTransDate>10/10/2018</SaleTransDate>
<Address>123 MG Road, 219435</Address>
<Invoice>
<InvoiceNumber>914200XXX3</InvoiceNumber>
<Amount>11453</Amount>
</Invoice>
<Invoice>
<InvoiceNumber>914200XXX3</InvoiceNumber>
<Amount>11453</Amount>
</Invoice>
<Invoice>
<InvoiceNumber>914200XXX3</InvoiceNumber>
<Amount>11453</Amount>
</Invoice>
</SalesOrder>
</CustomerInfoResponse>
When we are trying to add the new fetched value to parm methods of SubContract Class's(SalesOrder & Invoice), the old values that are already in the list are also overridden and that is why the last fetched value is looped in the list.
I believe that i should mark the parm methods of the subcontract as list as well which will then add the new values to it one by one, and to achieve that i have added AifCollectionTypeAttribute(), and List on the method signatures as shown below:
InvoiceDataContract:
[
DataContractAttribute("Invoice")
]
class InvoiceContract
{
List InvoiceNumber;
List Amount;
}
[
DataMemberAttribute("InvoiceNumber"),
AifCollectionTypeAttribute('return', Type::Class, classStr(InvoiceContract))
]
public List parmInvoiceNumber(List _InvoiceNumber = InvoiceNumber)
{
InvoiceNumber = _InvoiceNumber ;
return InvoiceNumber ;
}
[
DataMemberAttribute("Amount"),
AifCollectionTypeAttribute('return', Type::Class, classStr(InvoiceContract))
]
public List parmAmount(List_Amount= Amount)
{
Amount= _Amount;
return Amount;
}
But i am facing issue, as we have errors when consuming the Service through VS.
I request you to help me to add list of values to parm methods of a DataContract(Invoice Contract) class and adding that particular DataContract to another parm method of DataContract(SalesOrder Contract).
Thank you in advance!!
What you did can't work - the return value can't have three different types at once.
CustInfoUnsuccesfullResponse is not needed at all - throw an exception instead.
Then you can either create two separate service operations for invoices and packing slips, or you can merge CustInfoInvoiveResponse and CustInfoPackingResponse to a single class handling both cases.
Hi
In addition to the above scenario, If their is no Invoice found for the related SalesOrder, we have a different contract which should return the Packing slip details.
To achieve the required scenario, i have created a three different contract classes to return different schema:
1. Unsuccessful response
2. Sales order & Invoice response
3. Sales order & Packing slip response
Below is the development i followed -
Service Operation:
[
SysEntryPointAttribute(true),
AifCollectionTypeAttribute('return', Type::Class, classStr(CustInfoUnsuccesfullResponse)),
AifCollectionTypeAttribute('return', Type::Class, classStr(CustInfoInvoiveResponse)),
AifCollectionTypeAttribute('return', Type::Class, classStr(CustInfoPackingResponse))
]
public List customerInfo(CustomerNumber custID, TransDate date)
{
List _custInfoUnsuccesfullList = new List(Types::Class);
List _custInfoInvoiceList = new List(Types::Class);
List _custInfoPackingList = new List(Types::Class);
custInfoUnsuccesfullResponse = new CustInfoUnsuccesfullResponse();
custInfoInvoiceResponse = new CustInfoInvoiveResponse();
custInfoPackingResponse = new CustInfoPackingResponse();
if (custID == '')
{
custInfoUnsuccesfullResponse .parmDescription('Enter Customer ID');
_custInfoUnsuccesfullList .addEnd(custInfoUnsuccesfullResponse);
return _custInfoUnsuccesfullList ;
}
//After the filtration from Sales table
if (invoceID)
{
custInfoInvoiceResponse.parmCustId(custId);
custInfoInvoiceResponse .parmDate(date);
custInfoInvoiceResponse .parmSalesId(salesTrans.SalesId);
custInfoInvoiceResponse .parmInvoice(salesTrans.InvoiceId);
_custInfoInvoiceList .addEnd(custInfoInvoiceResponse);
return _custInfoInvoiceList ;
}
else
{
custInfoPackingResponse.parmCustId(custId);
custInfoPackingResponse .parmDate(date);
custInfoInvoiceResponse .parmSalesId(salesTrans.SalesId);
custInfoPackingResponse .parmPacking(salesTrans.Packing);
_custInfoPackingList .addEnd(custInfoPackingResponse);
return _custInfoPackingList ;
}
}
After that i have added the reference to a visual studio project and tried to send the request but i am facing the below casting error when running the project after providing the inputs.
Error:
Unable to cast object of type
'Dynamics.AX.Application.CustInfoUnsuccesfullResponse' to type
'Dynamics.AX.Application.CustInfoPackingResponse'.
Note: In this example i have removed the nested list contract for Invoice and Packing details and their were no errors in the ax compilation also.
Request you all to let me know if i can declare three different collection type attributes in the same service operation so that depending on the logic any one class is returned, and if no - how can i handle my required scenario where i have to return three different xml schema.
Thank you in advance!!
We already discussed nested data contracts in your older threads (e.g. Data Contract Inside another data contract).
Regarding Lists, you need a property of type List plus the AifCollectionTypeAttribute describing the type inside the list. Like this:
[ DataMemberAttribute, AifCollectionTypeAttribute("_orders", Types::Class, classStr(OrderContract)), AifCollectionTypeAttribute("return", Types::Class, classStr(OrderContract)) ] public list parmOrders(List _orders = orders) { orders = _orders; return orders; }
Stay up to date on forum activity by subscribing. You can also customize your in-app and email Notification settings across all subscriptions.
André Arnaud de Cal... 291,240 Super User 2024 Season 2
Martin Dráb 230,149 Most Valuable Professional
nmaenpaa 101,156