A validation model

Signing and validating the estimate
This commit is contained in:
Paul Schneider 2016-12-08 18:10:01 +01:00
commit 044f145035
28 changed files with 492 additions and 190 deletions

View file

@ -1,56 +0,0 @@
using BookAStar.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XLabs.Forms.Mvvm;
namespace BookAStar.ViewModels
{
/// <summary>
/// Used to make the DataManager know how
/// to sync local and remote data
/// </summary>
public class EditingViewModel: ViewModel
{
bool existsRemotely;
public bool ExistsRemotely
{
get
{
return existsRemotely;
}
set
{
base.SetProperty<bool>(ref existsRemotely, value);
}
}
bool isValid;
public bool IsValid
{
get
{
return isValid;
}
set
{
base.SetProperty<bool>(ref isValid, value);
}
}
bool isDirty;
public bool IsDirty
{
get
{
return isDirty;
}
set
{
base.SetProperty<bool>(ref isDirty, value);
}
}
}
}

View file

@ -1,19 +1,54 @@
using BookAStar.Attributes;
using BookAStar.Interfaces;
using BookAStar.Model.Workflow;
using BookAStar.ViewModels.Validation;
using System;
using System.Globalization;
using System.Windows.Input;
using System.ComponentModel;
namespace BookAStar.ViewModels.EstimateAndBilling
{
public class BillingLineViewModel : EditingViewModel, IBillingLine
public class BillingLineViewModel : EditingViewModel<BillingLine>, IBillingLine
{
BillingLine data;
public ICommand RemoveCommand { get; set; }
public ICommand ValidateCommand { set; get; }
public BillingLineViewModel(BillingLine data)
public BillingLineViewModel(BillingLine data): base(data)
{
this.Data = data;
CheckCommand = new Action<BillingLine, ModelState>(
(l,s) => {
if (string.IsNullOrWhiteSpace(l.Description))
{
s.AddError("Description",Strings.NoDescription);
}
if (l.UnitaryCost < 0) { s.AddError("UnitaryCost", Strings.InvalidValue); }
if (l.Count < 0) { s.AddError("Count", Strings.InvalidValue); }
});
SyncData();
}
private void SyncData()
{
if (Data != null)
{
// set durationValue, durationUnit
Duration = Data.Duration;
// other redondant representation
count = Data.Count;
description = Data.Description;
unitaryCostText = Data.UnitaryCost.ToString("G", CultureInfo.InvariantCulture);
}
CheckCommand(Data, ViewModelState);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.PropertyName=="Data")
{
SyncData();
}
}
private int count;
@ -27,7 +62,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling
set
{
SetProperty<int>(ref count, value);
data.Count = count;
Data.Count = count;
}
}
private string description;
@ -41,10 +76,10 @@ namespace BookAStar.ViewModels.EstimateAndBilling
set
{
SetProperty<string>(ref description, value);
data.Description = description;
IsValid = !string.IsNullOrWhiteSpace(description);
Data.Description = description;
}
}
decimal unitaryCost;
public decimal UnitaryCost
{
@ -56,7 +91,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling
set
{
SetProperty<decimal>(ref unitaryCost, value);
data.UnitaryCost = unitaryCost;
Data.UnitaryCost = unitaryCost;
}
}
@ -71,7 +106,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling
set
{
SetProperty<int>(ref durationValue, value, "DurationValue");
data.Duration = this.Duration;
Data.Duration = this.Duration;
}
}
@ -94,7 +129,7 @@ pour décrire la quantité de travail associée à ce type de service")]
set
{
SetProperty<DurationUnits>(ref durationUnit, value, "DurationUnit");
data.Duration = this.Duration;
Data.Duration = this.Duration;
}
}
@ -120,7 +155,6 @@ pour décrire la quantité de travail associée à ce type de service")]
}
}
public ICommand ValidateCommand { set; get; }
public TimeSpan Duration
{
@ -161,26 +195,5 @@ pour décrire la quantité de travail associée à ce type de service")]
}
}
public BillingLine Data
{
get
{
return data;
}
set
{
SetProperty<BillingLine>(ref data, value);
if (data != null)
{
// sets durationValue & durationUnit
count = data.Count;
description = data.Description;
Duration = data.Duration;
unitaryCostText = data.UnitaryCost.ToString("G", CultureInfo.InvariantCulture);
}
}
}
}
}

View file

@ -6,18 +6,13 @@ using Xamarin.Forms;
using BookAStar.Data;
using Newtonsoft.Json;
using System.Linq;
using BookAStar.ViewModels.Validation;
using System.ComponentModel;
namespace BookAStar.ViewModels.EstimateAndBilling
{
public class EditEstimateViewModel : EditingViewModel
public class EditEstimateViewModel : EditingViewModel<Estimate>
{
/// <summary>
/// For deserialization
/// </summary>
public EditEstimateViewModel()
{
}
/// <summary>
/// Builds a new view model on estimate,
@ -25,11 +20,37 @@ namespace BookAStar.ViewModels.EstimateAndBilling
/// </summary>
/// <param name="data"></param>
/// <param name="localState"></param>
public EditEstimateViewModel(Estimate data)
public EditEstimateViewModel(Estimate data) : base(data)
{
Data = data;
SyncData();
}
private void SyncData()
{
if (Data.AttachedFiles == null) Data.AttachedFiles = new List<string>();
if (Data.AttachedGraphics == null) Data.AttachedGraphics = new List<string>();
if (Data.Bill == null) Data.Bill = new List<BillingLine>();
AttachedFiles = new ObservableCollection<string>(Data.AttachedFiles);
AttachedGraphicList = new ObservableCollection<string>(Data.AttachedGraphics);
Bill = new ObservableCollection<BillingLineViewModel>(Data.Bill.Select(
l => new BillingLineViewModel(l)
));
Bill.CollectionChanged += Bill_CollectionChanged;
Title = Data.Title;
Description = Data.Description;
NotifyPropertyChanged("FormattedTotal");
NotifyPropertyChanged("Query");
NotifyPropertyChanged("CLient");
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.PropertyName == "Data")
{
SyncData();
}
}
/// <summary>
///
/// </summary>
@ -42,25 +63,6 @@ namespace BookAStar.ViewModels.EstimateAndBilling
NotifyPropertyChanged("Bill");
}
private Estimate data;
public Estimate Data { get { return data; } set {
SetProperty<Estimate>(ref data, value);
if (data.AttachedFiles == null) data.AttachedFiles = new List<string>();
if (data.AttachedGraphics == null) data.AttachedGraphics = new List<string>();
if (data.Bill == null) data.Bill = new List<BillingLine>();
AttachedFiles = new ObservableCollection<string>(data.AttachedFiles);
AttachedGraphicList = new ObservableCollection<string>(data.AttachedGraphics);
Bill = new ObservableCollection<BillingLineViewModel>(data.Bill.Select(
l => new BillingLineViewModel(l)
));
Bill.CollectionChanged += Bill_CollectionChanged;
Title = Data.Title;
Description = Data.Description;
NotifyPropertyChanged("FormattedTotal");
NotifyPropertyChanged("Query");
NotifyPropertyChanged("CLient");
} }
[JsonIgnore]
public ObservableCollection<string> AttachedFiles
{
@ -124,7 +126,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling
{
get
{
OnPlatform<Font> lfs = (OnPlatform<Font>)App.Current.Resources["LargeFontSize"];
OnPlatform<Font> lfs = (OnPlatform<Font>)App.Current.Resources["MediumFontSize"];
OnPlatform<Color> etc = (OnPlatform<Color>)App.Current.Resources["EmphasisTextColor"];
return new FormattedString

View file

@ -0,0 +1,12 @@
using XLabs.Forms.Mvvm;
namespace BookAStar.ViewModels
{
internal class PageState
{
internal int Position { get; set; }
internal object BindingContext { get; set; }
internal string PageType { get; set; }
}
}

View file

@ -6,9 +6,8 @@ namespace BookAStar.ViewModels.Signing
{
public class EstimateSigningViewModel: EditEstimateViewModel
{
public EstimateSigningViewModel(Estimate document)
public EstimateSigningViewModel(Estimate document): base(document)
{
Data = document;
}
public Command<bool> ValidationCommand { get; set; }
}

View file

@ -0,0 +1,83 @@
using BookAStar.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using XLabs.Forms.Mvvm;
using System.ComponentModel;
namespace BookAStar.ViewModels.Validation
{
/// <summary>
/// Used to make the DataManager know how
/// to sync local and remote data
/// </summary>
public class EditingViewModel<DataType>: ViewModel
{
public Action<DataType, ModelState> CheckCommand { set; get; }
public DataType Data { get; set; }
private ModelState viewModelState = new ModelState();
public ModelState ViewModelState
{
get
{
return viewModelState;
}
set
{
base.SetProperty<ModelState>(ref viewModelState, value);
}
}
public EditingViewModel(DataType data)
{
this.Data = data;
ViewModelState = new ModelState();
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (CheckCommand != null)
{
ViewModelState.Clear();
CheckCommand(Data, ViewModelState);
}
}
/* NOTE : I had a dream.
bool existsRemotely;
public bool ExistsRemotely
{
get
{
return existsRemotely;
}
set
{
base.SetProperty<bool>(ref existsRemotely, value);
}
}
bool isDirty;
public bool IsDirty
{
get
{
return isDirty;
}
set
{
base.SetProperty<bool>(ref isDirty, value);
}
}
*/
}
}

View file

@ -0,0 +1,13 @@
namespace BookAStar.ViewModels.Validation
{
public class InputError
{
public InputError(string errorMessage, ErrorSeverity severity)
{
Text = errorMessage;
Severity = severity;
}
public string Text { get; set; }
public ErrorSeverity Severity { get; set; }
}
}

View file

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BookAStar.ViewModels.Validation
{
public enum ErrorSeverity
{
Crippling,
Bearable
}
}

View file

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace BookAStar.ViewModels.Validation
{
public class ModelState : BindableObject
{
public static readonly BindableProperty IsValidProperty =
BindableProperty.Create("IsValid", typeof(bool), typeof(ModelState), false);
public static readonly BindableProperty ErrorsProperty =
BindableProperty.Create("Errors", typeof(Dictionary<string,List<InputError>>), typeof(ModelState), null);
public ModelState()
{
Errors = new Dictionary<string, List<InputError>>();
}
public bool IsValid
{
get
{
return (bool) GetValue(IsValidProperty);
}
}
public Dictionary<string, List<InputError>> Errors
{
get
{
return (Dictionary<string, List<InputError>>)GetValue(ErrorsProperty);
}
set
{
SetValue(ErrorsProperty, value);
}
}
public virtual void AddError(string propertyName, string errorMessage, ErrorSeverity severity = ErrorSeverity.Crippling)
{
InputError e = new InputError(errorMessage, severity) ;
if (Errors.ContainsKey(propertyName))
{
var errList = Errors[propertyName];
errList.Add(e);
}
else
{
Errors.Add(propertyName, new List<InputError>(new InputError [] { e }));
}
if (e.Severity < ErrorSeverity.Bearable)
SetValue(IsValidProperty, false);
}
public virtual void Clear ()
{
Errors.Clear();
SetValue(IsValidProperty, true);
}
}
}