Browse Source

Add Cancel button in order detail view when the status is submitted and add requestId guid to create a new order

pull/223/merge
Christian Arenas 7 years ago
parent
commit
83e425da2b
14 changed files with 226 additions and 45 deletions
  1. +12
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Orders/CancelOrderCommand.cs
  2. +1
    -1
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Orders/Order.cs
  3. +2
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/IOrderService.cs
  4. +10
    -6
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderMockService.cs
  5. +27
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderService.cs
  6. +28
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/RequestProvider/HttpRequestExceptionEx.cs
  7. +2
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/RequestProvider/IRequestProvider.cs
  8. +23
    -1
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/RequestProvider/RequestProvider.cs
  9. +1
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/CheckoutViewModel.cs
  10. +51
    -3
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/OrderDetailViewModel.cs
  11. +9
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/MainView.xaml.cs
  12. +57
    -32
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/OrderDetailView.xaml
  13. +1
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/Templates/OrderItemTemplate.xaml
  14. +2
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj

+ 12
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Orders/CancelOrderCommand.cs View File

@ -0,0 +1,12 @@
namespace eShopOnContainers.Core.Models.Orders
{
public class CancelOrderCommand
{
public int OrderNumber { get; }
public CancelOrderCommand(int orderNumber)
{
OrderNumber = orderNumber;
}
}
}

+ 1
- 1
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Orders/Order.cs View File

@ -54,6 +54,6 @@ namespace eShopOnContainers.Core.Models.Orders
public decimal Total { get; set; }
[JsonProperty("ordernumber")]
public string OrderNumber { get; set; }
public int OrderNumber { get; set; }
}
}

+ 2
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/IOrderService.cs View File

@ -2,6 +2,7 @@
using System.Collections.ObjectModel;
using System.Threading.Tasks;
namespace eShopOnContainers.Core.Services.Order
{
public interface IOrderService
@ -10,6 +11,7 @@ namespace eShopOnContainers.Core.Services.Order
Task<ObservableCollection<Models.Orders.Order>> GetOrdersAsync(string token);
Task<Models.Orders.Order> GetOrderAsync(int orderId, string token);
Task<ObservableCollection<Models.Orders.CardType>> GetCardTypesAsync(string token);
Task<bool> CancelOrderAsync(int orderId, string token);
BasketCheckout MapOrderToBasket(Models.Orders.Order order);
}
}

+ 10
- 6
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderMockService.cs View File

@ -47,10 +47,10 @@ namespace eShopOnContainers.Core.Services.Order
private List<Models.Orders.Order> MockOrders = new List<Models.Orders.Order>()
{
new Models.Orders.Order { OrderNumber = "1", SequenceNumber = 123, OrderDate = DateTime.Now, OrderStatus = OrderStatus.Submitted, OrderItems = MockOrderItems, CardTypeId = MockPaymentInfo.CardType.Id, CardHolderName = MockPaymentInfo.CardHolderName, CardNumber = MockPaymentInfo.CardNumber, CardSecurityNumber = MockPaymentInfo.SecurityNumber, CardExpiration = new DateTime(MockPaymentInfo.ExpirationYear, MockPaymentInfo.ExpirationMonth, 1), ShippingCity = MockAdress.City, ShippingState = MockAdress.State, ShippingCountry = MockAdress.Country, ShippingStreet = MockAdress.Street, Total = 36.46M },
new Models.Orders.Order { OrderNumber = "2", SequenceNumber = 132, OrderDate = DateTime.Now, OrderStatus = OrderStatus.Paid, OrderItems = MockOrderItems, CardTypeId = MockPaymentInfo.CardType.Id, CardHolderName = MockPaymentInfo.CardHolderName, CardNumber = MockPaymentInfo.CardNumber, CardSecurityNumber = MockPaymentInfo.SecurityNumber, CardExpiration = new DateTime(MockPaymentInfo.ExpirationYear, MockPaymentInfo.ExpirationMonth, 1), ShippingCity = MockAdress.City, ShippingState = MockAdress.State, ShippingCountry = MockAdress.Country, ShippingStreet = MockAdress.Street, Total = 36.46M },
new Models.Orders.Order { OrderNumber = "3", SequenceNumber = 231, OrderDate = DateTime.Now, OrderStatus = OrderStatus.Cancelled, OrderItems = MockOrderItems, CardTypeId = MockPaymentInfo.CardType.Id, CardHolderName = MockPaymentInfo.CardHolderName, CardNumber = MockPaymentInfo.CardNumber, CardSecurityNumber = MockPaymentInfo.SecurityNumber, CardExpiration = new DateTime(MockPaymentInfo.ExpirationYear, MockPaymentInfo.ExpirationMonth, 1), ShippingCity = MockAdress.City, ShippingState = MockAdress.State, ShippingCountry = MockAdress.Country, ShippingStreet = MockAdress.Street, Total = 36.46M },
new Models.Orders.Order { OrderNumber = "4", SequenceNumber = 131, OrderDate = DateTime.Now, OrderStatus = OrderStatus.Shipped, OrderItems = MockOrderItems, CardTypeId = MockPaymentInfo.CardType.Id, CardHolderName = MockPaymentInfo.CardHolderName, CardNumber = MockPaymentInfo.CardNumber, CardSecurityNumber = MockPaymentInfo.SecurityNumber, CardExpiration = new DateTime(MockPaymentInfo.ExpirationYear, MockPaymentInfo.ExpirationMonth, 1), ShippingCity = MockAdress.City, ShippingState = MockAdress.State, ShippingCountry = MockAdress.Country, ShippingStreet = MockAdress.Street, Total = 36.46M }
new Models.Orders.Order { OrderNumber = 1, SequenceNumber = 123, OrderDate = DateTime.Now, OrderStatus = OrderStatus.Submitted, OrderItems = MockOrderItems, CardTypeId = MockPaymentInfo.CardType.Id, CardHolderName = MockPaymentInfo.CardHolderName, CardNumber = MockPaymentInfo.CardNumber, CardSecurityNumber = MockPaymentInfo.SecurityNumber, CardExpiration = new DateTime(MockPaymentInfo.ExpirationYear, MockPaymentInfo.ExpirationMonth, 1), ShippingCity = MockAdress.City, ShippingState = MockAdress.State, ShippingCountry = MockAdress.Country, ShippingStreet = MockAdress.Street, Total = 36.46M },
new Models.Orders.Order { OrderNumber = 2, SequenceNumber = 132, OrderDate = DateTime.Now, OrderStatus = OrderStatus.Paid, OrderItems = MockOrderItems, CardTypeId = MockPaymentInfo.CardType.Id, CardHolderName = MockPaymentInfo.CardHolderName, CardNumber = MockPaymentInfo.CardNumber, CardSecurityNumber = MockPaymentInfo.SecurityNumber, CardExpiration = new DateTime(MockPaymentInfo.ExpirationYear, MockPaymentInfo.ExpirationMonth, 1), ShippingCity = MockAdress.City, ShippingState = MockAdress.State, ShippingCountry = MockAdress.Country, ShippingStreet = MockAdress.Street, Total = 36.46M },
new Models.Orders.Order { OrderNumber = 3, SequenceNumber = 231, OrderDate = DateTime.Now, OrderStatus = OrderStatus.Cancelled, OrderItems = MockOrderItems, CardTypeId = MockPaymentInfo.CardType.Id, CardHolderName = MockPaymentInfo.CardHolderName, CardNumber = MockPaymentInfo.CardNumber, CardSecurityNumber = MockPaymentInfo.SecurityNumber, CardExpiration = new DateTime(MockPaymentInfo.ExpirationYear, MockPaymentInfo.ExpirationMonth, 1), ShippingCity = MockAdress.City, ShippingState = MockAdress.State, ShippingCountry = MockAdress.Country, ShippingStreet = MockAdress.Street, Total = 36.46M },
new Models.Orders.Order { OrderNumber = 4, SequenceNumber = 131, OrderDate = DateTime.Now, OrderStatus = OrderStatus.Shipped, OrderItems = MockOrderItems, CardTypeId = MockPaymentInfo.CardType.Id, CardHolderName = MockPaymentInfo.CardHolderName, CardNumber = MockPaymentInfo.CardNumber, CardSecurityNumber = MockPaymentInfo.SecurityNumber, CardExpiration = new DateTime(MockPaymentInfo.ExpirationYear, MockPaymentInfo.ExpirationMonth, 1), ShippingCity = MockAdress.City, ShippingState = MockAdress.State, ShippingCountry = MockAdress.Country, ShippingStreet = MockAdress.Street, Total = 36.46M }
};
private static List<OrderItem> MockOrderItems = new List<OrderItem>()
@ -99,8 +99,7 @@ namespace eShopOnContainers.Core.Services.Order
if (!string.IsNullOrEmpty(token))
return MockOrders
.FirstOrDefault(o => o.OrderNumber.Equals(orderId.ToString(),
StringComparison.CurrentCultureIgnoreCase));
.FirstOrDefault(o => o.OrderNumber.Equals(orderId));
else
return new Models.Orders.Order();
}
@ -119,5 +118,10 @@ namespace eShopOnContainers.Core.Services.Order
{
return MockBasketCheckout;
}
public Task<bool> CancelOrderAsync(int orderId, string token)
{
return Task.FromResult(true);
}
}
}

+ 27
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderService.cs View File

@ -2,7 +2,9 @@
using eShopOnContainers.Core.Services.RequestProvider;
using System;
using System.Collections.ObjectModel;
using System.Net.Http;
using System.Threading.Tasks;
using eShopOnContainers.Core.Models.Orders;
namespace eShopOnContainers.Core.Services.Order
{
@ -88,5 +90,30 @@ namespace eShopOnContainers.Core.Services.Order
Street = order.ShippingStreet
};
}
public async Task<bool> CancelOrderAsync(int orderId, string token)
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.OrdersEndpoint);
builder.Path = "api/v1/orders/cancel";
var cancelOrderCommand = new CancelOrderCommand(orderId);
string uri = builder.ToString();
var header = "x-requestid";
try
{
await _requestProvider.PutAsync(uri, cancelOrderCommand, token, header);
}
//If the status of the order has changed before to click cancel button, we will get
//a BadRequest HttpStatus
catch (HttpRequestExceptionEx ex) when (ex.HttpCode == System.Net.HttpStatusCode.BadRequest)
{
return false;
}
return true;
}
}
}

+ 28
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/RequestProvider/HttpRequestExceptionEx.cs View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace eShopOnContainers.Core.Services.RequestProvider
{
public class HttpRequestExceptionEx : HttpRequestException
{
public System.Net.HttpStatusCode HttpCode { get; }
public HttpRequestExceptionEx(System.Net.HttpStatusCode code) : this(code, null, null)
{
}
public HttpRequestExceptionEx(System.Net.HttpStatusCode code, string message) : this (code, message, null)
{
}
public HttpRequestExceptionEx(System.Net.HttpStatusCode code, string message, Exception inner) : base(message,
inner)
{
HttpCode = code;
}
}
}

+ 2
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/RequestProvider/IRequestProvider.cs View File

@ -10,6 +10,8 @@ namespace eShopOnContainers.Core.Services.RequestProvider
Task<TResult> PostAsync<TResult>(string uri, string data, string clientId, string clientSecret);
Task<TResult> PutAsync<TResult>(string uri, TResult data, string token = "", string header = "");
Task DeleteAsync(string uri, string token = "");
}
}

+ 23
- 1
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/RequestProvider/RequestProvider.cs View File

@ -83,6 +83,28 @@ namespace eShopOnContainers.Core.Services.RequestProvider
return result;
}
public async Task<TResult> PutAsync<TResult>(string uri, TResult data, string token = "", string header = "")
{
HttpClient httpClient = CreateHttpClient(token);
if (!string.IsNullOrEmpty(header))
{
AddHeaderParameter(httpClient, header);
}
var content = new StringContent(JsonConvert.SerializeObject(data));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await httpClient.PutAsync(uri, content);
await HandleResponse(response);
string serialized = await response.Content.ReadAsStringAsync();
TResult result = await Task.Run(() =>
JsonConvert.DeserializeObject<TResult>(serialized, _serializerSettings));
return result;
}
public async Task DeleteAsync(string uri, string token = "")
{
HttpClient httpClient = CreateHttpClient(token);
@ -135,7 +157,7 @@ namespace eShopOnContainers.Core.Services.RequestProvider
throw new ServiceAuthenticationException(content);
}
throw new HttpRequestException(content);
throw new HttpRequestExceptionEx(response.StatusCode, content);
}
}
}


+ 1
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/CheckoutViewModel.cs View File

@ -133,6 +133,7 @@ namespace eShopOnContainers.Core.ViewModels
var authToken = Settings.AuthAccessToken;
var basket = _orderService.MapOrderToBasket(Order);
basket.RequestId = Guid.NewGuid();
// Create basket checkout
await _basketService.CheckoutAsync(basket, authToken);


+ 51
- 3
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/OrderDetailViewModel.cs View File

@ -3,14 +3,18 @@ using eShopOnContainers.Core.Models.Orders;
using eShopOnContainers.Core.ViewModels.Base;
using eShopOnContainers.Core.Services.Order;
using System;
using System.Windows.Input;
using eShopOnContainers.Core.Helpers;
using Xamarin.Forms;
namespace eShopOnContainers.Core.ViewModels
{
public class OrderDetailViewModel : ViewModelBase
{
private IOrderService _ordersService;
private readonly IOrderService _ordersService;
private Order _order;
private bool _isSubmittedOrder;
private string _orderStatusText;
public OrderDetailViewModel(IOrderService ordersService)
{
@ -19,7 +23,7 @@ namespace eShopOnContainers.Core.ViewModels
public Order Order
{
get { return _order; }
get => _order;
set
{
_order = value;
@ -27,6 +31,29 @@ namespace eShopOnContainers.Core.ViewModels
}
}
public bool IsSubmittedOrder
{
get => _isSubmittedOrder;
set
{
_isSubmittedOrder = value;
RaisePropertyChanged(() => IsSubmittedOrder);
}
}
public string OrderStatusText
{
get => _orderStatusText;
set
{
_orderStatusText = value;
RaisePropertyChanged(() => OrderStatusText);
}
}
public ICommand ToggleCancelOrderCommand => new Command(async () => await ToggleCancelOrderAsync());
public override async Task InitializeAsync(object navigationData)
{
if (navigationData is Order)
@ -37,10 +64,31 @@ namespace eShopOnContainers.Core.ViewModels
// Get order detail info
var authToken = Settings.AuthAccessToken;
Order = await _ordersService.GetOrderAsync(Convert.ToInt32(order.OrderNumber), authToken);
Order = await _ordersService.GetOrderAsync(order.OrderNumber, authToken);
IsSubmittedOrder = Order.OrderStatus == OrderStatus.Submitted;
OrderStatusText = Order.OrderStatus.ToString().ToUpper();
IsBusy = false;
}
}
private async Task ToggleCancelOrderAsync()
{
var authToken = Settings.AuthAccessToken;
var result = await _ordersService.CancelOrderAsync(_order.OrderNumber, authToken);
if (result)
{
OrderStatusText = OrderStatus.Cancelled.ToString().ToUpper();
}
else
{
Order = await _ordersService.GetOrderAsync(Order.OrderNumber, authToken);
OrderStatusText = Order.OrderStatus.ToString().ToUpper();
}
IsSubmittedOrder = false;
}
}
}

+ 9
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/MainView.xaml.cs View File

@ -48,8 +48,17 @@ namespace eShopOnContainers.Core.Views
{
// Force basket view refresh every time we access it
await (BasketView.BindingContext as ViewModelBase).InitializeAsync(null);
}
else if (CurrentPage is CampaignView)
{
// Force campaign view refresh every time we access it
await (CampaignView.BindingContext as ViewModelBase).InitializeAsync(null);
}
else if (CurrentPage is ProfileView)
{
// Force profile view refresh every time we access it
await (ProfileView.BindingContext as ViewModelBase).InitializeAsync(null);
}
}
}
}

+ 57
- 32
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/OrderDetailView.xaml View File

@ -55,7 +55,14 @@
<Setter Property="HorizontalOptions"
Value="End" />
</Style>
<Style x:Key="CancelOrderButtonStyle"
TargetType="{x:Type Button}">
<Setter Property="TextColor"
Value="{StaticResource WhiteColor}" />
</Style>
<animations:StoryBoard
x:Key="OrderInfoAnimation"
Target="{x:Reference OrderInfo}">
@ -96,72 +103,89 @@
Animation="{StaticResource OrderItemsAnimation}" />
</EventTrigger>
</ContentPage.Triggers>
<ScrollView>
<Grid
BackgroundColor="{StaticResource BackgroundColor}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackLayout
HeightRequest="50"
Grid.Column="0"
Grid.Row="0"
IsVisible="{Binding IsSubmittedOrder}">
<Button
BackgroundColor="{StaticResource LightGreenColor}"
Command="{Binding ToggleCancelOrderCommand}"
Text="CANCEL ORDER"
Style="{StaticResource CancelOrderButtonStyle}">
</Button>
</StackLayout>
<ScrollView
Grid.Row="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- ORDER INFO -->
<Grid
<Grid
x:Name="OrderInfo"
Opacity="0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackLayout
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackLayout
Grid.Column="0"
Grid.Row="0"
Margin="12">
<Label
<Label
Text="ORDER NUMBER"
Style="{StaticResource OrderTitleStyle}"/>
<Label
<Label
Text="{Binding Order.OrderNumber, Converter={StaticResource ToUpperConverter}}"
Style="{StaticResource OrderContentStyle}"/>
</StackLayout>
<StackLayout
</StackLayout>
<StackLayout
Grid.Column="0"
Grid.Row="1"
Margin="12">
<Label
<Label
Text="TOTAL"
Style="{StaticResource OrderTitleStyle}"/>
<Label
<Label
Text="{Binding Order.Total, StringFormat='${0:N}', Converter={StaticResource ToUpperConverter}}"
Style="{StaticResource OrderContentStyle}"/>
</StackLayout>
<StackLayout
</StackLayout>
<StackLayout
Grid.Column="1"
Grid.Row="0"
Margin="12">
<Label
<Label
Text="DATE"
Style="{StaticResource OrderTitleStyle}"/>
<Label
<Label
Text="{Binding Order.OrderDate, Converter={StaticResource DatetimeConverter}}"
Style="{StaticResource OrderContentStyle}"/>
</StackLayout>
<StackLayout
</StackLayout>
<StackLayout
Grid.Column="1"
Grid.Row="1"
Margin="12">
<Label
<Label
Text="STATUS"
Style="{StaticResource OrderTitleStyle}"/>
<Label
Text="{Binding Order.OrderStatus, Converter={StaticResource ToUpperConverter}}"
<Label
Text="{Binding OrderStatusText}"
Style="{StaticResource OrderContentStyle}"/>
</StackLayout>
</Grid>
<!-- SHIPPING ADDRESS -->
</StackLayout>
</Grid>
<!-- SHIPPING ADDRESS -->
<Grid
x:Name="ShippingAddress"
Grid.Row="1"
@ -248,4 +272,5 @@
</ActivityIndicator>
</Grid>
</ScrollView>
</Grid>
</ContentPage>

+ 1
- 2
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/Templates/OrderItemTemplate.xaml View File

@ -59,8 +59,7 @@
</ContentView.Resources>
<ContentView.Content>
<Grid
HeightRequest="120"
BackgroundColor="{StaticResource BackgroundColor}">
HeightRequest="120">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />


+ 2
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj View File

@ -75,6 +75,7 @@
<Compile Include="Models\Marketing\CampaignItem.cs" />
<Compile Include="Models\Marketing\CampaignRoot.cs" />
<Compile Include="Models\Navigation\TabParameter.cs" />
<Compile Include="Models\Orders\CancelOrderCommand.cs" />
<Compile Include="Models\Orders\CardType.CS" />
<Compile Include="Models\Orders\Order.cs" />
<Compile Include="Models\Orders\OrderItem.cs" />
@ -107,6 +108,7 @@
<Compile Include="Services\Order\IOrderService.cs" />
<Compile Include="Services\Order\OrderMockService.cs" />
<Compile Include="Services\Order\OrderService.cs" />
<Compile Include="Services\RequestProvider\HttpRequestExceptionEx.cs" />
<Compile Include="Services\RequestProvider\IRequestProvider.cs" />
<Compile Include="Services\RequestProvider\RequestProvider.cs" />
<Compile Include="Services\User\IUserService.cs" />


Loading…
Cancel
Save