SAP and Xamarin

Challenges of Mobilizing SAP MM with Xamarin - Purchase Order Items Page


Overview

In the previous post, we finished the second page of our app, the Purchase Orders page. In this post, we are going to continue our project, and start implementing the next page, called Purchase Order Items in the same way as before. It's going to be a bit more complex, than the previous two pages, since the page contains a header, a listview, and a button at the bottom of the page that requires a bit more work!

Today lessons

  • How to build complex Xamarin.Forms pages
  • How to navigate between Xamarin.Forms pages back and forth
  • How to use IValueConverter


Create a New Model for Purchase Order Item

Since we are working on the Purchase Order Item page, we need a model as well. For this, let's create a model, named PurchaseOrderItem, by right clicking on the folder, Models >> Add >> New Item. Here let's choose the template Visual C# >> Code >> Class.



For a Purchase Order Item, we are going to store the following info: Id, Name, Confirmed Price, Quantity Confirmation Ratio, and a Status.

namespace SAPGoodsReceipt.Models
{
    public class PurchaseOrderItem
    {
        public string Id { get; set; }
        
        public string Name { get; set; }

        public string ConfirmedPrice { get; set; }

        public string QuantityConfirmationRatio { get; set; }

        public PurchaseOrderItemStatus Status { get; set; }
    }
}


Extend the Purchase Order Model

Since, the Purchase Order Items belong to the Purchase Orders, we need to link them together like this: we are going to store a collection of Purchase Order Items for each Purchase Order. Then we add an additional property, named Total Price Confirmation Ratio.


Create an Enum for Purchase Order Item Statuses

In the model, Purchase Order Item we referred to a type, named PurchaseOrderItemStatus that actually is an enum.

To keep our projects transparent, we are going to create a new folder for our helper classes. For this, let's create a new folder, named Helpers under our PCL project, by right clicking on the project >> Add >> New Folder, where we are going to place the different helper classes of our app.



Now, let's create the enum, named PurchaseOrderItemStatus, by right clicking on the folder, Helpers >> Add >> New Item. Here let's choose the template Visual C# >> Code >> Class.



Within the class, let's implement an enum with the followings: New, InComplete, and Complete.

namespace SAPGoodsReceipt.Helpers
{
    public enum PurchaseOrderItemStatus
    {
        New,
        InComplete,
        Complete
    }
}


Create a Purchase Order Items Page

Now, we are going to create a new page for showing the items of a Purchase Order, by right clicking on the folder, Pages >> Add >> New Item. Here let's choose the template Visual C# >> Code >> Forms Xaml Page, and name it as PurchaseOrderItemsPage.


Construct the Purchase Order Items Page in XAML

This goes similar to the previous page, like we define a main StackLayout for holding the content of the complete page, and add a 15dip left&right padding, to ensure the content of our page doesn't climb down from the screen. Why we place the main StackLayout within ContentPage.Content? I will answer it later!

Then, we divide the page into three different sections: Purchase Order Header, Purchase Order Items, and a last one section for the Send Goods Receipt button.

Now, we are going to build the Header section first, using two another sub StackLayouts: one for the Vendor Name and the Number of Items, and another for the Ratio of the Confirmed Total Price that we aligned to the end (right side).

For the Vendor Name and the Number of Items, we use the exact same label definitions as in the Purchase Orders page.

The story is the same for the next one, but now instead of the Total Price, we are going to show the Ratio of the Confirmed Total Price.

The next section is about the List of Purchase Order Items that actually is ListView and we are going to define its ViewCell again, in the exact same way as we did in the case of the Purchase Orders listview.

The ViewCell definition starts with a simple StackLayout.

Then, we divide it into two section: a "left" and a "right" part.

On the left hand side, we place a Label for the Name of an Item, and display the Ratio of the Confirmed Quantity.

On the right hand side, we place a single Label for showing the Confirmed Price for the given Purchase Order Item.

In the last section, we only define a simple button, named Send Goods Receipt that we are going to use later on.


Colorize the Confirmed Price

As you remember, in the model, named PurchaseOrderItem we defined a property, called Status with an enum type that we will use to colorize the Confirmed Price. The rule is the following:

  • if the Status == InComplete, then the color is going to be Orange,
  • if the Status == Complete, then the color is going to be Green,
  • otherwise, the color is going to be the default color.

In XAML, to define the color of an object in this way, we need an IValueConverter implementation, let's say a StatusToColorConverter that is responsible to take a Status, and return a Color back. For this, let's create a new helper class, named StatusToColorConverter, by right clicking on the folder, Helpers >> Add >> New Item. Here let's choose the template Visual C# >> Code >> Class.



The implementation of the StatusToColorConverter is very simple. Now, we only need to implement the method, Convert(), so we won't deal with the method, ConvertBack(), just return a null back. Within the Convert() method, we are going to construct a simple branch based on the received value (that is actually a PurchaseOrderItemStatus), and return a color back.

That's it!

namespace SAPGoodsReceipt.Helpers
{
    public class StatusToColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, 
                              object parameter, System.Globalization.CultureInfo culture)
        {
            if ((PurchaseOrderItemStatus)value == PurchaseOrderItemStatus.Complete)
            {
                return Color.FromHex("#A6D800");
            }
            else if ((PurchaseOrderItemStatus)value == PurchaseOrderItemStatus.InComplete)
            {
                return Color.FromHex("#ED810E");
            }
            else
            {
                return Color.Default;
            }
        }

        public object ConvertBack(object value, Type targetType, 
                                  object parameter, System.Globalization.CultureInfo culture)
        {
            return null;
        }
    }
}

Now, it's time to jump back into the XAML definition. To use our newly created converter class, first we need to set its namespace and assembly. Then we need to build a simple ResourceDictionary, where we refer to it with the key colorConventer.

That's why, we used ContentPage.Content when we defined the layout of the page, because I knew that we are going to build a resource dictionary as well (ContentPage.Resources).

From now on, we can use it to define the color of our Confirmed Price label. So we bind the the ConfirmedPrice property to the Label's text, and the Status property to the Label's text color.

That's it!


Set Navigation to the Purchase Order Items Page

For this, we need to open up the XAML for PurchaseOrdersPage, and add a single line of code: ItemTapped = "OnPurchaseOrderSelected".

Now, there is nothing left, than implementing the method that we referred to in the XAML file, OnPurchaseOrderSelected().

When we tap on one of the item in the listview, the system will call this method. Our first task is to remove the row selection (otherwise the selected row will remain highlighted, when we navigate back to the list). Then we need to perform a simple navigation to our Purchase Order Items page, as initializing a new PurchaseOrderItemsPage with sending the selected Purchase Order as a parameter that then we add to the top of the navigation stack, using the Navigation.PushAsync() method.


Implement the Code-Behind

Now, as we are ready the navigation from the PurchaseOrdersPage to the PurchaseOrderItemsPage, we should catch somehow the received selected Purchase Order, and display its content on the page.

namespace SAPGoodsReceipt.Pages
{
    public partial class PurchaseOrderItemsPage : ContentPage
    {
        readonly PurchaseOrder PurchaseOrder;

        public PurchaseOrderItemsPage(PurchaseOrder purchaseOrder)
        {
            InitializeComponent();
            this.PurchaseOrder = purchaseOrder;

            ...
        }
    }
}

Then using the received Purchase Order, we feed the Title (simply display the PurchaseOrder.Id), the Header, and the Listview with data.

namespace SAPGoodsReceipt.Pages
{
    public partial class PurchaseOrderItemsPage : ContentPage
    {
        readonly PurchaseOrder PurchaseOrder;

        public PurchaseOrderItemsPage(PurchaseOrder purchaseOrder)
        {
            InitializeComponent();
            this.PurchaseOrder = purchaseOrder;

            Title = String.Format("Purchase Order: {0}", PurchaseOrder.Id);
            
            HeaderBinding();
            ListviewBinding();
        }
    }
}

In the Header section, we display the Name of the Vendor, the Number of Items, and at last the ratio for the Confirmed Total Price that we both get from the selected Purchase Order.

namespace SAPGoodsReceipt.Pages
{
    public partial class PurchaseOrderItemsPage : ContentPage
    {
        readonly PurchaseOrder PurchaseOrder;

        public PurchaseOrderItemsPage(PurchaseOrder purchaseOrder)
        {
            InitializeComponent();
            this.PurchaseOrder = purchaseOrder;

            Title = String.Format("Purchase Order: {0}", PurchaseOrder.Id);
            
            HeaderBinding();
            ListviewBinding();
        }

        private void HeaderBinding()
        {
            Name.BindingContext = PurchaseOrder;
            NumberOfItems.BindingContext = PurchaseOrder;
            TotalPriceConfirmationRatio.BindingContext = PurchaseOrder;
        }
    }
}

In the ListView, we simply bind the list of Items for the selected Purchase Order to our PurchaseOrderItemsListView.

namespace SAPGoodsReceipt.Pages
{
    public partial class PurchaseOrderItemsPage : ContentPage
    {
        readonly PurchaseOrder PurchaseOrder;

        public PurchaseOrderItemsPage(PurchaseOrder purchaseOrder)
        {
            InitializeComponent();
            this.PurchaseOrder = purchaseOrder;

            Title = String.Format("Purchase Order: {0}", PurchaseOrder.Id);
            
            HeaderBinding();
            ListviewBinding();
        }

        private void HeaderBinding()
        {
            Name.BindingContext = PurchaseOrder;
            NumberOfItems.BindingContext = PurchaseOrder;
            TotalPriceConfirmationRatio.BindingContext = PurchaseOrder;
        }

        private void ListviewBinding()
        {
            PurchaseOrderItemsListView.ItemsSource = PurchaseOrder.Items;
            BindingContext = this;
        }
    }
}


Extend the GetPurchaseOrders()

Finally, we arrived to the last part of our task today, namely we need to provide some sample data to our app (we need items for our Purchase Orders). We need to extend our existing method, GetPurchaseOrders().


Run Your Android and iOS Apps

Now comes the best part of building native mobile apps, namely let's BUILD and RUN it! :) I attached two screenshots about the running apps, one for the Android, and another for the iOS app.



Summary

It was a long story again, sorry for that! Hard work pays off! Now, we have a running app with three pages with a nice navigation among them! I hope, you could follow me, and everything went fine. If not, then don't hesitate to leave me a comment below!

Next, we are going to work on the Confirm Item page.

Stay tuned, keep reading! If you want to get notification about the newest posts, follow us or subscribe to our newsletter! If you liked it, please share it! Thanks!

blog comments powered by Disqus