Work with 1С web-service from Android application

When working with front-office for the cafe, the task appeared to access 1С web-service from application developed on Android. Google gave me several answers on how to work with SOAP using the library ksoap2-android. They helped to transfer the simple types, but when it comes to transfer an array, I had to think a little.

Web-service on 1С side

In 1С configuration web-service is created with method WriteSale. Method receives several parameters one of which, items, has the type ItemsSold (specified in the XDTO-package of configuration). Other parameters have the simple types (string, datetime). Configuration screenshot:

Publications: Work with 1С web-service from Android application

ItemsSold type has a single property Items for which a property «Maximum number» is set in -1 indicating that this is an array. The type of this property — ItemSold:

Publications: Work with 1С web-service from Android application

All the properties of ItemSold type has the simple type. Method WriteSale of web-service has the following code:

Function WriteSale(Id, Date, ClientCardNumber, DiscountRate, Items, DeptId, Bonuses, PremiumBonuses)
    Text = "OK";
    Try
        Card = GetCardByNumber(ClientCardNumber);
        PercentageDiscount = Number(DiscountRate);
        Division = FindDivByPrefix(DeptId);
        //...
        
        Document = FindDocumentByCodeDate(Date, Number(Id), Division);
        If Not ValueIsFilled(Document) Then
            Object = Documents.Sale.CreateDocument();
            Object.Date = Date;
            Object.CafeDocumentNumber = Id;
        Else
            Object = Document.GetObject();
        EndIf;
        
        Object.PaymentBonuses = Number(Bonuses);
        //...
        Object.Sale.Clear();
        For Each Item In Items.Items Do
            Nomenclature = FindGoodsByCode(Item.Code);
            Row = Object.Sale.Add();
            Row.Count = Number(Item.Quantity);
            Row.Nomenclature = Nomenclature;
            //...
        EndDo;
        
        Object.Write(DocumentWriteMode.Posting);
        
    Except
        Text = ErrorDescription();
        WriteLogEvent("Cafe.WriteSale - error: " + Text, EventLogLevel.Error);
        Raise;
    EndTry;
    // return error text or "OK"
    Return Text;
EndFunction 
Client on Android side

To access web-service from Android application, I wrote the following code:

protected String call() throws Exception {
            result = null;
            HttpTransportSE httpTransport = new HttpTransportSE(uri);
            httpTransport.debug = true;
            String resultString;

            SoapObject request = new SoapObject(namespace, methodName);
            request.addProperty("id", sale.getId());
            SimpleDateFormat dateFormat = new SimpleDateFormat(
                    "yyyy-MM-dd'T'HH:mm:ss");
            request.addProperty("date", dateFormat.format(sale.getDate()));
            request.addProperty("clientCardNumber", sale.getCardNumber());
            request.addProperty("bonuses", Double.toString(sale.getBonuses()));
            //...

            // see - http://code.google.com/p/ksoap2-android/wiki/CodingTipsAndTricks#Adding_an_array_of_complex_objects_to_the_request
            SoapObject sales = new SoapObject(namespace, "items");
            for (SaleItemInformation item : sale.getSales()) {
                SoapObject itemSoap = new SoapObject(namespace,
                        "Items");
                itemSoap.addProperty("Code", item.getItem().getSourceCode());
                itemSoap.addProperty("Quantity",
                        Double.toString(item.getQuantity()));
                //...
                sales.addSoapObject(itemSoap);
            }
            request.addSoapObject(sales);
            SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
                    SoapEnvelope.VER11);
            envelope.implicitTypes = true;
            envelope.setOutputSoapObject(request);
            try {
                httpTransport.call(soapAction, envelope);
            } catch (Exception e) {
                e.printStackTrace();
                throw e;
            }
            resultString = envelope.getResponse().toString();
            return resultString;
        }

It looks like the code is correct, generates a beautiful xml-request:

Thu May 31 16:13:08 YEKST 20121205.0113000100.02.0200.03001110.01.0110.0

But web-service responds to it with an error 500. At the same time, accessing another method with parameters of simple type on the same web-service, we receive the correct answer. Moreover, accessing from another 1С base using WS-reference the above web-service method, we receive the correct answer and execution of necessary actions on the web-service side. So, I had to intercept the query generated by another 1С base. I did not succeed to do this using the fiddler, because it has cut somehow the query body with xml and did not send it to the web-service. I succeeded in intercepting the query only using WireShark. So, the query text from 1С:

1234123
            12.2222
            122

It is easy to see that for the nested items of arrays (Code, Price…) the library ksoap2-android does not write down the prefixes with namespaces. For the root items (id, date…) they are also not written down, but this fact 1С does not consider as a stupor. And their absence in the sub-items causes the program to doubt in the correctness of input data, it cannot read them.

After examining the library code, I decided that the most efficient will be to modify the method SoapObject#addProperty(String, Object) in following way:

public static class SoapObjectCustom extends SoapObject {

        public SoapObjectCustom(String namespace, String name) {
            super(namespace, name);
        }

        @Override
        public SoapObject addProperty(String name, Object value) {
            PropertyInfo propertyInfo = new PropertyInfo();
            propertyInfo.name = name;
            propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS
                    : value.getClass();
            propertyInfo.setValue(value);

            // add this line
            propertyInfo.setNamespace(this.namespace);

            return addProperty(propertyInfo);
        }
    }

in the initial code I replaced the objects SoapObject with SoapObjectCustom in the following places:

//...
SoapObjectCustom request = new SoapObjectCustom(namespace, methodName);
//...
SoapObject sales = new SoapObject(namespace, "items");
for (SaleItemInformation item : sale.getSales()) {
    SoapObjectCustom itemSoap = new SoapObjectCustom(namespace,
            "Items");
    //...
}
//...
Conclusion

Most likely, there is a sense that the authors did not include the prefixes of namespace in the properties of items. And it is quite possible that in working with other web-services such adjustments will lead to incorrect program behaviour. Nevertheless, this method works with 1С web-services, I hope that this description will help someone in the work.

Described above has been tested with 1С 8.2.15.294 and Android 12 (3.0).

Click to rate this post!
[Total: 0 Average: 0]

Leave a Reply

Your email address will not be published. Required fields are marked *