Skip to content

Instantly share code, notes, and snippets.

@bentayloruk
Forked from ToJans/csharp.cs
Last active August 29, 2015 13:56
Show Gist options
  • Save bentayloruk/9218078 to your computer and use it in GitHub Desktop.
Save bentayloruk/9218078 to your computer and use it in GitHub Desktop.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace TDDCoverage
{
public class Order
{
public class Line
{
public readonly String ArticleId;
public readonly decimal Amount;
public Line(String ArticleId, decimal Amount)
{
this.ArticleId = ArticleId;
this.Amount = Amount;
}
}
public readonly IEnumerable<Line> Lines;
public Order(IEnumerable<Line> Lines)
{
this.Lines = Lines;
}
public class LineList : List<Line>
{
public void Add(String ArticleId, decimal Amount)
{
this.Add(new Line(ArticleId,Amount));
}
}
}
public class Invoice
{
public class Item
{
public readonly String Name;
public readonly decimal Amount, Price, VAT;
public Item(String Name, decimal Amount, decimal Price, decimal VAT)
{
this.Name = Name;
this.Amount = Amount;
this.Price = Price;
this.VAT = VAT;
}
public override bool Equals(object obj)
{
var x = obj as Item;
return x != null &&
x.Name == Name &&
x.Price == Price &&
x.VAT == VAT &&
x.Amount == Amount;
}
public override int GetHashCode()
{
return Name.GetHashCode() ^ Price.GetHashCode() ^ VAT.GetHashCode() ^ Amount.GetHashCode();
}
}
public readonly IEnumerable<Item> Items;
public Invoice(IEnumerable<Item> Items)
{
this.Items = Items;
}
public class ItemList:List<Item>
{
public void Add(String Name, decimal Amount, decimal Price, decimal VAT)
{
this.Add(new Item(Name,Amount,Price,VAT));
}
}
}
public enum VATCode
{
Unknown,
Regular,
Food,
None
}
public class VATRates : Dictionary<VATCode, decimal> { }
public class Article
{
public readonly string Id;
public readonly VATCode VATCode;
public readonly string Name;
public readonly decimal Price;
public Article(string Id, string Name, decimal Price, VATCode VAT)
{
this.Id = Id;
this.Name = Name;
this.Price = Price;
this.VATCode = VAT;
}
public class Dictionary : Dictionary<string, Article>
{
public void Add(string Id, string Name, int Price, VATCode VATCode)
{
this.Add(Id, new Article(Id, Name, Price, VATCode));
}
}
}
public class ArticleNotFoundException : Exception { }
public class VATRateNotFoundException : Exception { }
class Invoicer
{
private VATRates VATRates;
private Article.Dictionary Articles;
public Invoicer(VATRates VATRates, Article.Dictionary Articles)
{
this.VATRates = VATRates;
this.Articles = Articles;
}
public Invoice BillOrder(Order o)
{
Invoice.ItemList items = new Invoice.ItemList();
foreach (var orderline in o.Lines)
{
Article article;
Decimal vatcode;
if (!Articles.TryGetValue(orderline.ArticleId, out article)) throw new ArticleNotFoundException();
if (!VATRates.TryGetValue(article.VATCode, out vatcode)) throw new VATRateNotFoundException();
var total = orderline.Amount * article.Price;
items.Add(article.Name, orderline.Amount, total, total * vatcode);
}
return new Invoice(items);
}
}
[TestClass]
public class Bill_order_tests
{
Invoicer SUT;
Order.LineList OrderLines;
string unknown_article_id, article_id_with_unknown_vat;
[TestInitialize]
public void Setup()
{
var rates = new VATRates();
rates.Add(VATCode.Regular, .21M);
rates.Add(VATCode.Food, .06M);
rates.Add(VATCode.None, 0);
var articles = new Article.Dictionary();
articles.Add("HTC-123", "HTC One Two TTT", 1000, VATCode.Regular);
articles.Add("APPL", "Apple blah", 1, VATCode.Food);
articles.Add("RNV", "Stairs renovation", 1500, VATCode.None);
articles.Add("???", "VAT Unknown", 123, VATCode.Unknown);
SUT = new Invoicer(rates, articles);
OrderLines = new Order.LineList();
OrderLines.Add("HTC-123", 1);
OrderLines.Add("APPL", 12);
OrderLines.Add("RNV", 1);
unknown_article_id = "XYZ";
article_id_with_unknown_vat = "???";
}
[TestMethod]
public void Bill_an_order()
{
var invoice = SUT.BillOrder(new Order(OrderLines));
var invoiceItems = invoice.Items.ToArray();
var expectedItems = new Invoice.ItemList();
expectedItems.Add("HTC One Two TTT", 1, 1000, 210);
expectedItems.Add("Apple blah", 12, 12, 0.72M);
expectedItems.Add("Stairs renovation", 1, 1500, 0);
Assert.AreEqual(invoiceItems[0], expectedItems[0]);
Assert.AreEqual(invoiceItems[1], expectedItems[1]);
Assert.AreEqual(invoiceItems[2], expectedItems[2]);
}
[TestMethod]
[ExpectedException(typeof(ArticleNotFoundException))]
public void Bill_an_order_with_an_unkown_article()
{
OrderLines.Add(unknown_article_id, 5);
SUT.BillOrder(new Order(OrderLines));
}
[TestMethod]
[ExpectedException(typeof(VATRateNotFoundException))]
public void Bill_an_order_with_an_unknown_VAT_code()
{
OrderLines.Add(article_id_with_unknown_vat, 5);
SUT.BillOrder(new Order(OrderLines));
}
}
}
namespace UnitTestProject1
open System
//Don't need first | when all on one line :)
type VATType = Unknown | Regular | Food | NoVat
type VatRates = Map<VATType, decimal>
//Type alias for ArticleId so reads clearly in Map declaration.
type ArticleId = string
type Article = {ArticleId: ArticleId; Name: string; Price: decimal; VATCode: VATType}
//Made this map like in your C# code.
type Articles = Map<ArticleId,Article>
type OrderLine = { ArticleId: ArticleId; Amount: decimal }
type OrderLines = List<OrderLine>
type Order = { Lines: OrderLines}
type InvoiceItem = {Name:string; Amount: decimal; Price: decimal; VAT: decimal}
type Invoice = { Items: List<InvoiceItem>}
exception ArticleNotFoundException
exception VATRateNotFoundException
module Invoicer =
let BillOrder (order: Order) (vatrates: VatRates) (articles: Articles) =
let invoiceLines =
order.Lines
|> Seq.map (fun line ->
//Get article price AND VAT rate or die.
let articlePrice, vatRate =
match articles.TryFind line.ArticleId with
| Some article ->
match vatrates.TryFind article.VATCode with
| Some vatRate -> article.Price, vatRate
| None -> raise VATRateNotFoundException
| None -> raise ArticleNotFoundException
let total = articlePrice * line.Amount
//Not sure about this bit as was not complete in yours.
{ Name=""; Amount=total; Price=articlePrice; VAT=vatRate*total }
) |> List.ofSeq
{ Items = invoiceLines}
@bentayloruk
Copy link
Author

This version should have the same behaviour as the original (although I've not run it). Would suggest the exceptions are removed. You could return a DU from BillOrder with Success or a Failure of errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment