Commit 4b3170a

mo khan <mo@mokhan.ca>
2011-03-22 18:49:26
add domain and specs.
1 parent 313c9ca
product/desktop.ui/solidware.financials.csproj
@@ -60,8 +60,12 @@
     <Reference Include="Autofac">
       <HintPath>..\..\thirdparty\autofac\Autofac.dll</HintPath>
     </Reference>
-    <Reference Include="Castle.Core, Version=2.5.1.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL" />
-    <Reference Include="Db4objects.Db4o, Version=8.0.184.15484, Culture=neutral, PublicKeyToken=6199cd4f203aa8eb, processorArchitecture=MSIL" />
+    <Reference Include="Castle.Core">
+      <HintPath>..\..\thirdparty\castle\Castle.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="Db4objects.Db4o, Version=8.0.184.15484, Culture=neutral, PublicKeyToken=6199cd4f203aa8eb, processorArchitecture=MSIL">
+      <HintPath>..\..\thirdparty\db4o\Db4objects.Db4o.dll</HintPath>
+    </Reference>
     <Reference Include="gorilla.infrastructure">
       <HintPath>..\..\thirdparty\commons\gorilla.infrastructure.dll</HintPath>
     </Reference>
product/service/domain/accounting/Account.cs
@@ -0,0 +1,9 @@
+namespace solidware.financials.service.domain.accounting
+{
+    public interface Account
+    {
+        Quantity balance();
+        Quantity balance(Date date);
+        Quantity balance(Range<Date> period);
+    }
+}
\ No newline at end of file
product/service/domain/accounting/BOED.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace solidware.financials.service.domain.accounting
+{
+    public class BOED : SimpleUnitOfMeasure
+    {
+        public override string pretty_print(double amount)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
\ No newline at end of file
product/service/domain/accounting/ConversionRatio.cs
@@ -0,0 +1,18 @@
+namespace solidware.financials.service.domain.accounting
+{
+    public class ConversionRatio
+    {
+        double rate;
+        static public readonly ConversionRatio Default = new ConversionRatio(1);
+
+        public ConversionRatio(double rate)
+        {
+            this.rate = rate;
+        }
+
+        public double applied_to(double amount)
+        {
+            return amount*rate;
+        }
+    }
+}
\ No newline at end of file
product/service/domain/accounting/Currency.cs
@@ -0,0 +1,57 @@
+using gorilla.utility;
+
+namespace solidware.financials.service.domain.accounting
+{
+    public class Currency : SimpleUnitOfMeasure
+    {
+        static public readonly Currency USD = new Currency("USD");
+        static public readonly Currency CAD = new Currency("CAD");
+
+        Currency(string pneumonic)
+        {
+            this.pneumonic = pneumonic;
+        }
+
+        public override string pretty_print(double amount)
+        {
+            return "{0:C} {1}".format(amount, this);
+        }
+
+        public bool Equals(Currency other)
+        {
+            if (ReferenceEquals(null, other)) return false;
+            if (ReferenceEquals(this, other)) return true;
+            return Equals(other.pneumonic, pneumonic);
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj)) return false;
+            if (ReferenceEquals(this, obj)) return true;
+            if (obj.GetType() != typeof (Currency)) return false;
+            return Equals((Currency) obj);
+        }
+
+        public override int GetHashCode()
+        {
+            return (pneumonic != null ? pneumonic.GetHashCode() : 0);
+        }
+
+        static public bool operator ==(Currency left, Currency right)
+        {
+            return Equals(left, right);
+        }
+
+        static public bool operator !=(Currency left, Currency right)
+        {
+            return !Equals(left, right);
+        }
+
+        public override string ToString()
+        {
+            return pneumonic;
+        }
+
+        string pneumonic;
+    }
+}
\ No newline at end of file
product/service/domain/accounting/DateRange.cs
@@ -0,0 +1,15 @@
+namespace solidware.financials.service.domain.accounting
+{
+    static public class DateRange
+    {
+        static public Range<Date> up_to(Date date)
+        {
+            return Date.First.to(date);
+        }
+
+        static public Range<Date> to(this Date start, Date end)
+        {
+            return new SequentialRange<Date>(start, end);
+        }
+    }
+}
\ No newline at end of file
product/service/domain/accounting/Deposit.cs
@@ -0,0 +1,10 @@
+namespace solidware.financials.service.domain.accounting
+{
+    public class Deposit : TransactionType
+    {
+        public Quantity adjust(Quantity balance, Quantity adjustment)
+        {
+            return balance.plus(adjustment);
+        }
+    }
+}
\ No newline at end of file
product/service/domain/accounting/DetailAccount.cs
@@ -0,0 +1,67 @@
+using System.Collections.Generic;
+using gorilla.utility;
+using solidware.financials.service.domain.payroll;
+
+namespace solidware.financials.service.domain.accounting
+{
+    public class DetailAccount : Account, Visitable<Entry>
+    {
+        DetailAccount(UnitOfMeasure unit_of_measure)
+        {
+            this.unit_of_measure = unit_of_measure;
+        }
+
+        static public DetailAccount New(UnitOfMeasure unit_of_measure)
+        {
+            return new DetailAccount(unit_of_measure);
+        }
+
+        public void deposit(double amount)
+        {
+            deposit(amount, unit_of_measure);
+        }
+
+        public void deposit(double amount, UnitOfMeasure units)
+        {
+            add(Entry.New<Deposit>(amount, units));
+        }
+
+        public void withdraw(double amount, UnitOfMeasure units)
+        {
+            add(Entry.New<Withdrawal>(amount, units));
+        }
+
+        public void add(Entry new_entry)
+        {
+            entries.Add(new_entry);
+        }
+
+        public Quantity balance()
+        {
+            return balance(Calendar.now());
+        }
+
+        public Quantity balance(Date date)
+        {
+            return balance(DateRange.up_to(date));
+        }
+
+        public Quantity balance(Range<Date> period)
+        {
+            var result = new Quantity(0, unit_of_measure);
+            accept(new AnonymousVisitor<Entry>(x =>
+            {
+                if (x.booked_in(period)) result = x.adjust(result);
+            }));
+            return result;
+        }
+
+        public void accept(Visitor<Entry> visitor)
+        {
+            foreach (var entry in entries) visitor.visit(entry);
+        }
+
+        IList<Entry> entries = new List<Entry>();
+        UnitOfMeasure unit_of_measure;
+    }
+}
\ No newline at end of file
product/service/domain/accounting/Entry.cs
@@ -0,0 +1,34 @@
+namespace solidware.financials.service.domain.accounting
+{
+    public class Entry
+    {
+        static public Entry New<Transaction>(double amount, UnitOfMeasure units) where Transaction : TransactionType, new()
+        {
+            return New<Transaction>(new Quantity(amount, units));
+        }
+
+        static public Entry New<Transaction>(Quantity amount) where Transaction : TransactionType, new()
+        {
+            return new Entry
+                   {
+                       when_booked = Calendar.now(),
+                       transaction_type = new Transaction(),
+                       amount = amount,
+                   };
+        }
+
+        public virtual Quantity adjust(Quantity balance)
+        {
+            return transaction_type.adjust(balance, amount);
+        }
+
+        public virtual bool booked_in(Range<Date> period)
+        {
+            return period.includes(when_booked);
+        }
+
+        Date when_booked;
+        TransactionType transaction_type;
+        Quantity amount;
+    }
+}
\ No newline at end of file
product/service/domain/accounting/MCF.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace solidware.financials.service.domain.accounting
+{
+    public class MCF : SimpleUnitOfMeasure
+    {
+        public override string pretty_print(double amount)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
\ No newline at end of file
product/service/domain/accounting/Potential.cs
@@ -0,0 +1,27 @@
+namespace solidware.financials.service.domain.accounting
+{
+    public class Potential<Transaction> : PotentialEntry where Transaction : TransactionType, new()
+    {
+        static public Potential<Transaction> New(DetailAccount destination, Quantity amount)
+        {
+            return new Potential<Transaction>
+                   {
+                       account = destination,
+                       amount = amount,
+                   };
+        }
+
+        public Quantity combined_with(Quantity other)
+        {
+            return new Transaction().adjust(other, amount);
+        }
+
+        public void commit()
+        {
+            account.add(Entry.New<Transaction>(amount));
+        }
+
+        DetailAccount account;
+        Quantity amount;
+    }
+}
\ No newline at end of file
product/service/domain/accounting/PotentialEntry.cs
@@ -0,0 +1,8 @@
+namespace solidware.financials.service.domain.accounting
+{
+    public interface PotentialEntry
+    {
+        Quantity combined_with(Quantity other);
+        void commit();
+    }
+}
\ No newline at end of file
product/service/domain/accounting/Quantity.cs
@@ -0,0 +1,64 @@
+using System;
+
+namespace solidware.financials.service.domain.accounting
+{
+    public class Quantity : IEquatable<Quantity>
+    {
+        double amount;
+        UnitOfMeasure units;
+
+        public Quantity(double amount, UnitOfMeasure units)
+        {
+            this.units = units;
+            this.amount = amount;
+        }
+
+        public Quantity plus(Quantity other)
+        {
+            return new Quantity(amount + other.convert_to(units).amount, units);
+        }
+
+        public Quantity subtract(Quantity other)
+        {
+            return new Quantity(amount - other.convert_to(units).amount, units);
+        }
+
+        public Quantity convert_to(UnitOfMeasure unit_of_measure)
+        {
+            return new Quantity(unit_of_measure.convert(amount, units), unit_of_measure);
+        }
+
+        static public implicit operator double(Quantity quanity)
+        {
+            return quanity.amount;
+        }
+
+        public bool Equals(Quantity other)
+        {
+            if (ReferenceEquals(null, other)) return false;
+            if (ReferenceEquals(this, other)) return true;
+            return other.amount.Equals(amount) && Equals(other.units, units);
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj)) return false;
+            if (ReferenceEquals(this, obj)) return true;
+            if (obj.GetType() != typeof (Quantity)) return false;
+            return Equals((Quantity) obj);
+        }
+
+        public override int GetHashCode()
+        {
+            unchecked
+            {
+                return (amount.GetHashCode()*397) ^ (units != null ? units.GetHashCode() : 0);
+            }
+        }
+
+        public override string ToString()
+        {
+            return units.pretty_print(amount);
+        }
+    }
+}
\ No newline at end of file
product/service/domain/accounting/Range.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace solidware.financials.service.domain.accounting
+{
+    public interface Range<T> where T : IComparable<T>
+    {
+        bool includes(T item);
+    }
+}
\ No newline at end of file
product/service/domain/accounting/SequentialRange.cs
@@ -0,0 +1,27 @@
+using System;
+using gorilla.utility;
+
+namespace solidware.financials.service.domain.accounting
+{
+    public class SequentialRange<T> : Range<T> where T : IComparable<T>
+    {
+        readonly T start;
+        readonly T end;
+
+        public SequentialRange(T start, T end)
+        {
+            this.start = start;
+            this.end = end;
+        }
+
+        public bool includes(T item)
+        {
+            return item.CompareTo(start) >= 0 && item.CompareTo(end) <= 0;
+        }
+
+        public override string ToString()
+        {
+            return "{0} to {1}".format(start, end);
+        }
+    }
+}
\ No newline at end of file
product/service/domain/accounting/SimpleUnitOfMeasure.cs
@@ -0,0 +1,21 @@
+namespace solidware.financials.service.domain.accounting
+{
+    public delegate ConversionRatio RateTable(UnitOfMeasure unitCurrency, UnitOfMeasure referenceCurrency);
+
+    public abstract class SimpleUnitOfMeasure : UnitOfMeasure
+    {
+        public double convert(double amount, UnitOfMeasure other)
+        {
+            return rate_table(this, other).applied_to(amount);
+        }
+
+        public abstract string pretty_print(double amount);
+
+        static RateTable rate_table = (x, y) => ConversionRatio.Default;
+
+        static public void provide_rate(RateTable current_rates)
+        {
+            rate_table = current_rates;
+        }
+    }
+}
\ No newline at end of file
product/service/domain/accounting/SummaryAccount.cs
@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+using gorilla.utility;
+
+namespace solidware.financials.service.domain.accounting
+{
+    public class SummaryAccount : Account
+    {
+        static public SummaryAccount New(UnitOfMeasure unit_of_measure)
+        {
+            return new SummaryAccount
+                   {
+                       unit_of_measure = unit_of_measure
+                   };
+        }
+
+        public void add(Account account)
+        {
+            accounts.Add(account);
+        }
+
+        public Quantity balance()
+        {
+            return balance(Calendar.now());
+        }
+
+        public Quantity balance(Date date)
+        {
+            return balance(DateRange.up_to(date));
+        }
+
+        public Quantity balance(Range<Date> period)
+        {
+            var result = new Quantity(0, unit_of_measure);
+            accounts.each(x => result = result.plus(x.balance(period)));
+            return result;
+        }
+
+        ICollection<Account> accounts = new HashSet<Account>();
+        UnitOfMeasure unit_of_measure;
+    }
+}
\ No newline at end of file
product/service/domain/accounting/Transaction.cs
@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+using System.Linq;
+using gorilla.utility;
+
+namespace solidware.financials.service.domain.accounting
+{
+    public class Transaction
+    {
+        static public Transaction New(UnitOfMeasure reference_units)
+        {
+            return new Transaction(reference_units);
+        }
+
+        Transaction(UnitOfMeasure reference)
+        {
+            reference_units = reference;
+        }
+
+        public void deposit(DetailAccount destination, Quantity amount)
+        {
+            deposits.Add(Potential<Deposit>.New(destination, amount));
+        }
+
+        public void withdraw(DetailAccount source, Quantity amount)
+        {
+            withdrawals.Add(Potential<Withdrawal>.New(source, amount));
+        }
+
+        public void post()
+        {
+            ensure_zero_balance();
+            deposits.Union(withdrawals).each(x => x.commit());
+        }
+
+        void ensure_zero_balance()
+        {
+            var balance = calculate_total(deposits.Union(withdrawals));
+            if (balance == 0) return;
+
+            throw new TransactionDoesNotBalance();
+        }
+
+        Quantity calculate_total(IEnumerable<PotentialEntry> potential_transactions)
+        {
+            var result = new Quantity(0, reference_units);
+            potential_transactions.each(x => result = x.combined_with(result));
+            return result;
+        }
+
+        List<PotentialEntry> deposits = new List<PotentialEntry>();
+        List<PotentialEntry> withdrawals = new List<PotentialEntry>();
+        UnitOfMeasure reference_units;
+    }
+}
\ No newline at end of file
product/service/domain/accounting/TransactionDoesNotBalance.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace solidware.financials.service.domain.accounting
+{
+    public class TransactionDoesNotBalance : Exception
+    {
+        
+    }
+}
\ No newline at end of file
product/service/domain/accounting/TransactionType.cs
@@ -0,0 +1,7 @@
+namespace solidware.financials.service.domain.accounting
+{
+    public interface TransactionType
+    {
+        Quantity adjust(Quantity balance, Quantity adjustment);
+    }
+}
\ No newline at end of file
product/service/domain/accounting/UnitOfMeasure.cs
@@ -0,0 +1,9 @@
+namespace solidware.financials.service.domain.accounting
+{
+    public interface UnitOfMeasure
+    {
+        double convert(double amount, UnitOfMeasure other);
+        string pretty_print(double amount);
+    }
+
+}
\ No newline at end of file
product/service/domain/accounting/Withdrawal.cs
@@ -0,0 +1,10 @@
+namespace solidware.financials.service.domain.accounting
+{
+    public class Withdrawal : TransactionType
+    {
+        public Quantity adjust(Quantity balance, Quantity adjustment)
+        {
+            return balance.subtract(adjustment);
+        }
+    }
+}
\ No newline at end of file
product/service/domain/hierarchy/Hierarchy.cs
@@ -0,0 +1,65 @@
+using System.Collections.Generic;
+using System.Linq;
+using gorilla.utility;
+
+namespace solidware.financials.service.domain.hierarchy
+{
+    public class Hierarchy : Visitable<Hierarchy>
+    {
+        static readonly Hierarchy Null = new NullHierarchy();
+
+        public void accept(Visitor<Hierarchy> visitor)
+        {
+            visitor.visit(this);
+            children.each(x => x.accept(visitor));
+        }
+
+        public void add(Hierarchy child)
+        {
+            child.parent = this;
+            children.Add(child);
+        }
+
+        public void move_to(Hierarchy new_tree)
+        {
+            parent.remove(this);
+            new_tree.add(this);
+        }
+
+        public virtual void remove(Hierarchy descendant)
+        {
+            if (children.Contains(descendant))
+                children.Remove(descendant);
+            else
+            {
+                var hierarchy = children.SingleOrDefault(x => x.contains(descendant));
+                if(null != hierarchy) hierarchy.remove(descendant);
+            }
+        }
+
+        public bool contains(Hierarchy child)
+        {
+            return everyone().Any(x => x.Equals(child));
+        }
+
+        IEnumerable<Hierarchy> everyone()
+        {
+            var all = new List<Hierarchy> {this};
+            foreach (var child in children) all.AddRange(child.everyone());
+            return all;
+        }
+
+        public bool belongs_to(Hierarchy tree)
+        {
+            return tree.contains(this);
+        }
+
+        IList<Hierarchy> children = new List<Hierarchy>();
+        Hierarchy parent = Null;
+
+        class NullHierarchy : Hierarchy
+        {
+            public override void remove(Hierarchy descendant) {}
+        }
+    }
+}
\ No newline at end of file
product/service/domain/payroll/Annually.cs
@@ -0,0 +1,10 @@
+namespace solidware.financials.service.domain.payroll
+{
+    public class Annually : Frequency
+    {
+        public Date next(Date from_date)
+        {
+            return from_date.plus_years(1);
+        }
+    }
+}
\ No newline at end of file
product/service/domain/payroll/AnonymousVisitor.cs
@@ -0,0 +1,20 @@
+using System;
+using gorilla.utility;
+
+namespace solidware.financials.service.domain.payroll
+{
+    public class AnonymousVisitor<T> : Visitor<T>
+    {
+        Action<T> action;
+
+        public AnonymousVisitor(Action<T> action)
+        {
+            this.action = action;
+        }
+
+        public void visit(T item)
+        {
+            action(item);
+        }
+    }
+}
\ No newline at end of file
product/service/domain/payroll/BaseDenominator.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using gorilla.utility;
+
+namespace solidware.financials.service.domain.payroll
+{
+    public class BaseDenominator : Denominator
+    {
+        readonly int denominator;
+
+        protected BaseDenominator(int denominator)
+        {
+            this.denominator = denominator;
+        }
+
+        public IEnumerable<int> each_possible_value()
+        {
+            for (var i = 0; i < denominator; i++) yield return i;
+        }
+
+        public void each(Action<int> action)
+        {
+            each_possible_value().each(x => action(x));
+        }
+    }
+}
\ No newline at end of file
product/service/domain/payroll/Compensation.cs
@@ -0,0 +1,33 @@
+using System.Collections.Generic;
+using System.Linq;
+using gorilla.utility;
+
+namespace solidware.financials.service.domain.payroll
+{
+    public class Compensation : Visitable<Grant>
+    {
+        IList<Grant> grants = new List<Grant>();
+
+        public void issue_grant(Money grant_value, UnitPrice price, Fraction portion_to_issue_at_each_vest, Frequency frequency)
+        {
+            grants.Add(Grant.New(grant_value, price, portion_to_issue_at_each_vest, frequency));
+        }
+
+        public Grant grant_for(Date date)
+        {
+            return grants.Single(x => x.was_issued_on(date));
+        }
+
+        public Money unvested_balance(Date on_date)
+        {
+            var total = Money.Zero;
+            accept(new AnonymousVisitor<Grant>(grant => total = total.plus(grant.balance(on_date))));
+            return total;
+        }
+
+        public void accept(Visitor<Grant> visitor)
+        {
+            grants.each(x => visitor.visit(x));
+        }
+    }
+}
\ No newline at end of file
product/service/domain/payroll/Denominator.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+
+namespace solidware.financials.service.domain.payroll
+{
+    public interface Denominator
+    {
+        IEnumerable<int> each_possible_value();
+        void each(Action<int> action);
+    }
+}
\ No newline at end of file
product/service/domain/payroll/Fraction.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace solidware.financials.service.domain.payroll
+{
+    public interface Fraction
+    {
+        void each(Action<int> action);
+        int of(int number);
+    }
+}
\ No newline at end of file
product/service/domain/payroll/Frequency.cs
@@ -0,0 +1,7 @@
+namespace solidware.financials.service.domain.payroll
+{
+    public interface Frequency
+    {
+        Date next(Date from_date);
+    }
+}
\ No newline at end of file
product/service/domain/payroll/Grant.cs
@@ -0,0 +1,81 @@
+using System.Collections.Generic;
+
+namespace solidware.financials.service.domain.payroll
+{
+    public class Grant
+    {
+        Date issued_on;
+        History<UnitPrice> price_history = new History<UnitPrice>();
+        Units units = Units.Empty;
+        List<Vest> expirations = new List<Vest>();
+
+        static public Grant New(Money purchase_amount, UnitPrice price, Fraction portion, Frequency frequency)
+        {
+            var grant = new Grant
+                        {
+                            issued_on = Calendar.now(),
+                        };
+            grant.change_unit_price_to(price);
+            grant.purchase(purchase_amount);
+            grant.apply_vesting_frequency(portion, frequency);
+            return grant;
+        }
+
+        Grant() {}
+
+        public virtual void change_unit_price_to(UnitPrice new_price)
+        {
+            price_history.record(new_price);
+        }
+
+        public virtual bool was_issued_on(Date date)
+        {
+            return issued_on.Equals(date);
+        }
+
+        public virtual Money balance()
+        {
+            return balance(Calendar.now());
+        }
+
+        public virtual Money balance(Date on_date)
+        {
+            return unit_price(on_date).total_value_of(units_remaining(on_date));
+        }
+
+        Units units_remaining(Date on_date)
+        {
+            var remaining = Units.Empty;
+            foreach (var expiration in expirations)
+            {
+                remaining = remaining.combined_with(expiration.unvested_units(units, on_date));
+            }
+            return remaining;
+        }
+
+        void purchase(Money amount)
+        {
+            units = units.combined_with(current_unit_price().purchase_units(amount));
+        }
+
+        UnitPrice current_unit_price()
+        {
+            return unit_price(Calendar.now());
+        }
+
+        UnitPrice unit_price(Date on_date)
+        {
+            return price_history.recorded(on_date);
+        }
+
+        void apply_vesting_frequency(Fraction portion, Frequency frequency)
+        {
+            var next_date = issued_on.minus_days(1);
+            portion.each(x =>
+            {
+                next_date = frequency.next(next_date);
+                expirations.Add(new Vest(portion, next_date));
+            });
+        }
+    }
+}
\ No newline at end of file
product/service/domain/payroll/History.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace solidware.financials.service.domain.payroll
+{
+    public class History<T>
+    {
+        Stack<Event<T>> events = new Stack<Event<T>>();
+
+        public void record(T change)
+        {
+            events.Push(new Event<T>(change));
+        }
+
+        public T recorded(Date date)
+        {
+            return events.Where(x => x.occurred_on_or_before(date)).Max();
+        }
+
+        class Event<K> : IComparable<Event<K>>
+        {
+            public Event(K adjustment)
+            {
+                date_of_change = Calendar.now();
+                this.adjustment = adjustment;
+            }
+
+            K adjustment;
+            Date date_of_change;
+
+            public int CompareTo(Event<K> other)
+            {
+                return date_of_change.CompareTo(other.date_of_change);
+            }
+
+            public bool occurred_on_or_before(Date date)
+            {
+                return date_of_change.Equals(date) || date_of_change.is_before(date);
+            }
+
+            static public implicit operator K(Event<K> eventA)
+            {
+                return eventA.adjustment;
+            }
+        }
+    }
+}
\ No newline at end of file
product/service/domain/payroll/Money.cs
@@ -0,0 +1,61 @@
+using System;
+using gorilla.utility;
+
+namespace solidware.financials.service.domain.payroll
+{
+    public class Money : IEquatable<Money>
+    {
+        double value;
+        static public Money Zero = new Money(0);
+
+        Money(double value)
+        {
+            this.value = value;
+        }
+
+        static public implicit operator Money(double raw)
+        {
+            return new Money(raw);
+        }
+
+        public virtual Money plus(Money other)
+        {
+            return value + other.value;
+        }
+
+        public virtual Money minus(Money other)
+        {
+            return value - other.value;
+        }
+
+        public virtual bool Equals(Money other)
+        {
+            if (ReferenceEquals(null, other)) return false;
+            if (ReferenceEquals(this, other)) return true;
+            return other.value.Equals(value);
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj)) return false;
+            if (ReferenceEquals(this, obj)) return true;
+            if (obj.GetType() != typeof (Money)) return false;
+            return Equals((Money) obj);
+        }
+
+        public override int GetHashCode()
+        {
+            return value.GetHashCode();
+        }
+
+        public override string ToString()
+        {
+            return "{0:C}".format(value);
+        }
+
+        public Units at_price(double price)
+        {
+            return Units.New((int)(value / price));
+        }
+    }
+}
\ No newline at end of file
product/service/domain/payroll/Monthly.cs
@@ -0,0 +1,10 @@
+namespace solidware.financials.service.domain.payroll
+{
+    public class Monthly : Frequency
+    {
+        public Date next(Date from_date)
+        {
+            return from_date.add_months(1);
+        }
+    }
+}
\ No newline at end of file
product/service/domain/payroll/One.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Linq;
+
+namespace solidware.financials.service.domain.payroll
+{
+    public class One<T> : Fraction where T : Denominator, new()
+    {
+        T denominator = new T();
+
+        public void each(Action<int> action)
+        {
+            denominator.each(x => action(x));
+        }
+
+        public int of(int number)
+        {
+            return number / denominator.each_possible_value().Count();
+        }
+    }
+}
\ No newline at end of file
product/service/domain/payroll/Third.cs
@@ -0,0 +1,7 @@
+namespace solidware.financials.service.domain.payroll
+{
+    public class Third : BaseDenominator
+    {
+        public Third() : base(3) {}
+    }
+}
\ No newline at end of file
product/service/domain/payroll/Twelfth.cs
@@ -0,0 +1,7 @@
+namespace solidware.financials.service.domain.payroll
+{
+    public class Twelfth : BaseDenominator
+    {
+        public Twelfth() : base(12) {}
+    }
+}
\ No newline at end of file
product/service/domain/payroll/UnitPrice.cs
@@ -0,0 +1,27 @@
+namespace solidware.financials.service.domain.payroll
+{
+    public class UnitPrice
+    {
+        readonly double price;
+
+        UnitPrice(double price)
+        {
+            this.price = price;
+        }
+
+        static public implicit operator UnitPrice(double raw)
+        {
+            return new UnitPrice(raw);
+        }
+
+        public Units purchase_units(Money amount)
+        {
+            return amount.at_price(price);
+        }
+
+        public virtual Money total_value_of(Units units)
+        {
+            return units.value_at(price);
+        }
+    }
+}
\ No newline at end of file
product/service/domain/payroll/Units.cs
@@ -0,0 +1,30 @@
+namespace solidware.financials.service.domain.payroll
+{
+    public class Units
+    {
+        static public readonly Units Empty = new Units();
+        Units() {}
+
+        static public Units New(int units)
+        {
+            return new Units {units = units};
+        }
+
+        public Money value_at(double price)
+        {
+            return price*units;
+        }
+
+        public Units combined_with(Units other)
+        {
+            return New(units + other.units);
+        }
+
+        public Units reduced_by(Fraction portion)
+        {
+            return New(portion.of(units));
+        }
+
+        int units;
+    }
+}
\ No newline at end of file
product/service/domain/payroll/Vest.cs
@@ -0,0 +1,24 @@
+namespace solidware.financials.service.domain.payroll
+{
+    public class Vest
+    {
+        Fraction portion;
+        Date vesting_date;
+
+        public Vest(Fraction portion, Date vesting_date)
+        {
+            this.portion = portion;
+            this.vesting_date = vesting_date;
+        }
+
+        public Units unvested_units(Units total_units, Date date)
+        {
+            return expires_before(date) ? Units.Empty : total_units.reduced_by(portion);
+        }
+
+        bool expires_before(Date date)
+        {
+            return vesting_date.is_before(date);
+        }
+    }
+}
\ No newline at end of file
product/service/domain/property_bag/Bag.cs
@@ -0,0 +1,10 @@
+namespace solidware.financials.service.domain.property_bag
+{
+    public class Bag
+    {
+        static public PropertyBag For<Target>()
+        {
+            return new PropertyBagWithoutTarget<Target>();
+        }
+    }
+}
\ No newline at end of file
product/service/domain/property_bag/Property.cs
@@ -0,0 +1,7 @@
+namespace solidware.financials.service.domain.property_bag
+{
+    public interface Property
+    {
+        bool represents(string name);
+    }
+}
\ No newline at end of file
product/service/domain/property_bag/PropertyBag.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace solidware.financials.service.domain.property_bag
+{
+    public interface PropertyBag
+    {
+        Property property_named(string name);
+        IEnumerable<Property> all();
+    }
+}
\ No newline at end of file
product/service/domain/property_bag/PropertyBagWithoutTarget.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace solidware.financials.service.domain.property_bag
+{
+    public class PropertyBagWithoutTarget<T> : PropertyBag
+    {
+        IEnumerable<Property> all_properties;
+
+        public PropertyBagWithoutTarget()
+        {
+            all_properties = typeof (T).GetProperties().Select(x => (Property) new PropertyReference<T>(x));
+        }
+
+        public Property property_named(string name)
+        {
+            if (all_properties.Any(x => x.represents(name)))
+                all_properties.Single(x => x.represents(name));
+            return new UnknownProperty();
+        }
+
+        public IEnumerable<Property> all()
+        {
+            return all_properties;
+        }
+    }
+}
\ No newline at end of file
product/service/domain/property_bag/PropertyReference.cs
@@ -0,0 +1,19 @@
+using System.Reflection;
+
+namespace solidware.financials.service.domain.property_bag
+{
+    public class PropertyReference<Target> : Property
+    {
+        readonly PropertyInfo property;
+
+        public PropertyReference(PropertyInfo property)
+        {
+            this.property = property;
+        }
+
+        public bool represents(string name)
+        {
+            return string.Compare(property.Name, name, true) == 0;
+        }
+    }
+}
\ No newline at end of file
product/service/domain/property_bag/UnknownProperty.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace solidware.financials.service.domain.property_bag
+{
+    public class UnknownProperty : Property
+    {
+        public bool represents(string name)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
\ No newline at end of file
product/service/domain/Calendar.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace solidware.financials.service.domain
+{
+    static public class Calendar
+    {
+        static Func<Date> date = () => DateTime.Now.Date;
+        static Func<Date> default_date = () => DateTime.Now.Date;
+
+        static public void stop(Func<Date> new_date)
+        {
+            date = new_date;
+        }
+
+        static public void start()
+        {
+            date = default_date;
+        }
+
+        static public Date now()
+        {
+            return date();
+        }
+
+        static public void reset()
+        {
+            date = default_date;
+        }
+    }
+}
\ No newline at end of file
product/service/domain/ComparableExtensions.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace solidware.financials.service.domain
+{
+    static public class ComparableExtensions
+    {
+        static public bool is_before<T>(this T left, T right) where T : IComparable<T>
+        {
+            return left.CompareTo(right) < 0;
+        }
+
+        static public bool is_after<T>(this T left, T right) where T : IComparable<T>
+        {
+            return left.CompareTo(right) > 0;
+        }
+    }
+}
\ No newline at end of file
product/service/domain/Date.cs
@@ -0,0 +1,57 @@
+using System;
+using gorilla.utility;
+
+namespace solidware.financials.service.domain
+{
+    public class Date : IComparable<Date>
+    {
+        long ticks;
+        static public readonly Date First = new Date(DateTime.MinValue);
+        static public readonly Date Last = new Date(DateTime.MaxValue);
+
+        Date(DateTime date)
+        {
+            ticks = date.Date.Ticks;
+        }
+
+        static public implicit operator Date(DateTime raw)
+        {
+            return new Date(raw);
+        }
+
+        public bool Equals(Date other)
+        {
+            if (ReferenceEquals(null, other)) return false;
+            if (ReferenceEquals(this, other)) return true;
+            return other.ticks == ticks;
+        }
+
+        public int CompareTo(Date other)
+        {
+            return to_date_time().CompareTo(new DateTime(other.ticks));
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj)) return false;
+            if (ReferenceEquals(this, obj)) return true;
+            if (obj.GetType() != typeof (Date)) return false;
+            return Equals((Date) obj);
+        }
+
+        public override int GetHashCode()
+        {
+            return ticks.GetHashCode();
+        }
+
+        public override string ToString()
+        {
+            return "{0}".format(to_date_time());
+        }
+
+        public DateTime to_date_time()
+        {
+            return new DateTime(ticks);
+        }
+    }
+}
\ No newline at end of file
product/service/domain/DateExtensions.cs
@@ -0,0 +1,25 @@
+namespace solidware.financials.service.domain
+{
+    static public class DateExtensions
+    {
+        static public Date plus_years(this Date date, int years)
+        {
+            return date.to_date_time().AddYears(years);
+        }
+
+        static public Date plus_days(this Date date, int days)
+        {
+            return date.to_date_time().AddDays(days);
+        }
+
+        static public Date minus_days(this Date date, int days)
+        {
+            return date.to_date_time().AddDays(-days);
+        }
+
+        static public Date add_months(this Date date, int months)
+        {
+            return date.to_date_time().AddMonths(months);
+        }
+    }
+}
\ No newline at end of file
product/service/domain/Person.cs
@@ -1,5 +1,3 @@
-using gorilla.utility;
-
 namespace solidware.financials.service.domain
 {
     public class Person : Entity
product/service/service.csproj
@@ -31,7 +31,9 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="Castle.Core, Version=2.5.1.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL" />
+    <Reference Include="Castle.Core">
+      <HintPath>..\..\thirdparty\castle\Castle.Core.dll</HintPath>
+    </Reference>
     <Reference Include="Db4objects.Db4o">
       <HintPath>..\..\thirdparty\db4o\Db4objects.Db4o.dll</HintPath>
     </Reference>
@@ -52,6 +54,55 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="DB4OBootstrapper.cs" />
+    <Compile Include="domain\accounting\Account.cs" />
+    <Compile Include="domain\accounting\BOED.cs" />
+    <Compile Include="domain\accounting\ConversionRatio.cs" />
+    <Compile Include="domain\accounting\Currency.cs" />
+    <Compile Include="domain\accounting\DateRange.cs" />
+    <Compile Include="domain\accounting\Deposit.cs" />
+    <Compile Include="domain\accounting\DetailAccount.cs" />
+    <Compile Include="domain\accounting\Entry.cs" />
+    <Compile Include="domain\accounting\MCF.cs" />
+    <Compile Include="domain\accounting\Potential.cs" />
+    <Compile Include="domain\accounting\PotentialEntry.cs" />
+    <Compile Include="domain\accounting\Quantity.cs" />
+    <Compile Include="domain\accounting\Range.cs" />
+    <Compile Include="domain\accounting\SequentialRange.cs" />
+    <Compile Include="domain\accounting\SimpleUnitOfMeasure.cs" />
+    <Compile Include="domain\accounting\SummaryAccount.cs" />
+    <Compile Include="domain\accounting\Transaction.cs" />
+    <Compile Include="domain\accounting\TransactionDoesNotBalance.cs" />
+    <Compile Include="domain\accounting\TransactionType.cs" />
+    <Compile Include="domain\accounting\UnitOfMeasure.cs" />
+    <Compile Include="domain\accounting\Withdrawal.cs" />
+    <Compile Include="domain\Calendar.cs" />
+    <Compile Include="domain\ComparableExtensions.cs" />
+    <Compile Include="domain\Date.cs" />
+    <Compile Include="domain\DateExtensions.cs" />
+    <Compile Include="domain\hierarchy\Hierarchy.cs" />
+    <Compile Include="domain\payroll\Annually.cs" />
+    <Compile Include="domain\payroll\AnonymousVisitor.cs" />
+    <Compile Include="domain\payroll\BaseDenominator.cs" />
+    <Compile Include="domain\payroll\Compensation.cs" />
+    <Compile Include="domain\payroll\Denominator.cs" />
+    <Compile Include="domain\payroll\Fraction.cs" />
+    <Compile Include="domain\payroll\Frequency.cs" />
+    <Compile Include="domain\payroll\Grant.cs" />
+    <Compile Include="domain\payroll\History.cs" />
+    <Compile Include="domain\payroll\Money.cs" />
+    <Compile Include="domain\payroll\Monthly.cs" />
+    <Compile Include="domain\payroll\One.cs" />
+    <Compile Include="domain\payroll\Third.cs" />
+    <Compile Include="domain\payroll\Twelfth.cs" />
+    <Compile Include="domain\payroll\UnitPrice.cs" />
+    <Compile Include="domain\payroll\Units.cs" />
+    <Compile Include="domain\payroll\Vest.cs" />
+    <Compile Include="domain\property_bag\Bag.cs" />
+    <Compile Include="domain\property_bag\Property.cs" />
+    <Compile Include="domain\property_bag\PropertyBag.cs" />
+    <Compile Include="domain\property_bag\PropertyBagWithoutTarget.cs" />
+    <Compile Include="domain\property_bag\PropertyReference.cs" />
+    <Compile Include="domain\property_bag\UnknownProperty.cs" />
     <Compile Include="handlers\AddIncomeCommandMessageHandler.cs" />
     <Compile Include="handlers\AddNewFamilyMemberHandler.cs" />
     <Compile Include="domain\Entity.cs" />
product/specs/unit/service/domain/accounting/BOEDSpecs.cs
@@ -0,0 +1,47 @@
+using Machine.Specifications;
+using solidware.financials.service.domain.accounting;
+
+namespace specs.unit.service.domain.accounting
+{
+    public class BOEDSpecs
+    {
+        public abstract class concern 
+        {
+            Establish context = () =>
+            {
+                sut = new BOED();
+            };
+
+            Cleanup clean = () =>
+            {
+                SimpleUnitOfMeasure.provide_rate((x, y) => ConversionRatio.Default);
+            };
+
+            static protected BOED sut;
+        }
+
+        [Concern(typeof (BOED))]
+        public class when_converting_one_barrel_of_oil_equivalent_to_one_mcf : concern
+        {
+            Establish c = () =>
+            {
+                SimpleUnitOfMeasure.provide_rate((x, y) =>
+                {
+                    return new ConversionRatio(6);
+                });
+            };
+
+            Because b = () =>
+            {
+                result = sut.convert(1, new MCF());
+            };
+
+            It should_return_the_corrent_value = () =>
+            {
+                result.should_be_equal_to(6);
+            };
+
+            static double result;
+        }
+    }
+}
\ No newline at end of file
product/specs/unit/service/domain/accounting/CurrencySpecs.cs
@@ -0,0 +1,53 @@
+using Machine.Specifications;
+using solidware.financials.service.domain.accounting;
+
+namespace specs.unit.service.domain.accounting
+{
+    public class CurrencySpecs
+    {
+        public abstract class concern : runner<Currency>
+        {
+            Cleanup clean = () => { 
+                SimpleUnitOfMeasure.provide_rate((x, y) => ConversionRatio.Default);
+            };
+        }
+
+        [Concern(typeof (Currency))]
+        public class when_converting_one_USD_dollar_to_CAD : concern
+        {
+            Establish c = () =>
+            {
+                SimpleUnitOfMeasure.provide_rate((x, y) => new ConversionRatio(1.05690034));
+            };
+
+            It should_return_the_correct_amount = () =>
+            {
+                sut.convert(1, Currency.CAD).should_be_equal_to(1.05690034);
+            };
+
+            protected override Currency create_sut()
+            {
+                return Currency.USD;
+            }
+        }
+
+        [Concern(typeof (Currency))]
+        public class when_converting_one_CAD_dollar_to_USD : concern
+        {
+            Establish c = () =>
+            {
+                SimpleUnitOfMeasure.provide_rate((x, y) => new ConversionRatio(0.95057));
+            };
+
+            It should_return_the_correct_amount = () =>
+            {
+                sut.convert(1.05690034d, Currency.USD).should_be_equal_to(1.0046577561938002d);
+            };
+
+            protected override Currency create_sut()
+            {
+                return Currency.CAD;
+            }
+        }
+    }
+}
\ No newline at end of file
product/specs/unit/service/domain/accounting/DetailAccountSpecs.cs
@@ -0,0 +1,45 @@
+using Machine.Specifications;
+using solidware.financials.service.domain.accounting;
+
+namespace specs.unit.service.domain.accounting
+{
+    public class DetailAccountSpecs
+    {
+        public abstract class concern : runner<DetailAccount>
+        {
+            protected override DetailAccount create_sut()
+            {
+                return DetailAccount.New(Currency.CAD);
+            }
+        }
+
+        [Concern(typeof (DetailAccount))]
+        public class when_depositing_money_in_to_an_account : concern
+        {
+            Because b = () =>
+            {
+                sut.deposit(100.01, Currency.CAD);
+            };
+
+            It should_adjust_the_balance = () =>
+            {
+                sut.balance().should_be_equal_to(new Quantity(100.01, Currency.CAD));
+            };
+        }
+
+        [Concern(typeof (DetailAccount))]
+        public class when_withdrawing_money_from_an_account : concern
+        {
+            Because b = () =>
+            {
+                sut.deposit(100.01);
+                sut.withdraw(10.00, Currency.CAD);
+            };
+
+            It should_adjust_the_balance = () =>
+            {
+                sut.balance().should_be_equal_to(new Quantity(90.01, Currency.CAD));
+            };
+        }
+    }
+}
\ No newline at end of file
product/specs/unit/service/domain/accounting/QuantitySpecs.cs
@@ -0,0 +1,37 @@
+using Machine.Specifications;
+using solidware.financials.service.domain.accounting;
+
+namespace specs.unit.service.domain.accounting
+{
+    public class QuantitySpecs
+    {
+        public abstract class concern
+        {
+            Establish blah = () =>
+            {
+                sut = new Quantity(100, Currency.CAD);
+            };
+
+            static protected Quantity sut;
+        }
+
+        [Concern(typeof (Quantity))]
+        public class when_checking_if_two_quantities_are_equal : concern
+        {
+            It should_return_true_when_they_represent_the_same_amount_and_units = () =>
+            {
+                sut.should_be_equal_to(new Quantity(100, Currency.CAD));
+            };
+
+            It should_return_false_when_they_are_not_the_same_amount = () =>
+            {
+                sut.ShouldNotEqual(new Quantity(1, Currency.CAD));
+            };
+
+            It should_return_false_when_they_represent_different_currencies = () =>
+            {
+                sut.ShouldNotEqual(new Quantity(100, Currency.USD));
+            };
+        }
+    }
+}
\ No newline at end of file
product/specs/unit/service/domain/accounting/SummaryAccountSpecs.cs
@@ -0,0 +1,45 @@
+using Machine.Specifications;
+using solidware.financials.service.domain.accounting;
+
+namespace specs.unit.service.domain.accounting
+{
+    public class SummaryAccountSpecs
+    {
+        public abstract class concern : runner<SummaryAccount>
+        {
+            protected override SummaryAccount create_sut()
+            {
+                return SummaryAccount.New(Currency.CAD);
+            }
+        }
+
+        [Concern(typeof (SummaryAccount))]
+        public class when_retrieving_the_balance_from_a_summary_account : concern
+        {
+            Establish c = () =>
+            {
+                cash =  DetailAccount.New(Currency.CAD);
+                credit = DetailAccount.New(Currency.CAD);
+
+                cash.deposit(50, Currency.CAD);
+                credit.deposit(50, Currency.CAD);
+            };
+
+            Because b = () =>
+            {
+                sut.add(cash);
+                sut.add(credit);
+                result = sut.balance();
+            };
+
+            It should_sum_the_balance_for_each_detail_account = () =>
+            {
+                result.should_be_equal_to(new Quantity(100.00, Currency.CAD));
+            };
+
+            static Quantity result;
+            static DetailAccount cash;
+            static DetailAccount credit;
+        }
+    }
+}
\ No newline at end of file
product/specs/unit/service/domain/accounting/TransactionSpecs.cs
@@ -0,0 +1,117 @@
+using System;
+using Machine.Specifications;
+using solidware.financials.service.domain.accounting;
+
+namespace specs.unit.service.domain.accounting
+{
+    public class TransactionSpecs
+    {
+        public class concern : runner<Transaction>
+        {
+            protected override Transaction create_sut()
+            {
+                return Transaction.New(Currency.CAD);
+            }
+            Establish c = () => {
+                //sut = Activator.CreateInstance(GetType());
+                sut = new concern().create_sut();
+            };
+        }
+
+        [Concern(typeof (Transaction))]
+        public class when_transferring_funds_from_one_account_to_another : concern
+        {
+            Establish c = () =>
+            {
+                source_account = DetailAccount.New(Currency.CAD);
+                destination_account = DetailAccount.New(Currency.CAD);
+                source_account.add(Entry.New<Deposit>(100, Currency.CAD));
+            };
+
+            Because b = () =>
+            {
+                sut.deposit(destination_account, new Quantity(100, Currency.CAD));
+                sut.withdraw(source_account, new Quantity(100, Currency.CAD));
+                sut.post();
+            };
+
+            It should_increase_the_balance_of_the_destination_account = () =>
+            {
+                destination_account.balance().should_be_equal_to(new Quantity(100, Currency.CAD));
+            };
+
+            It should_decrease_the_balance_of_the_source_account = () =>
+            {
+                source_account.balance().should_be_equal_to(new Quantity(0, Currency.CAD));
+            };
+
+            static DetailAccount source_account;
+            static DetailAccount destination_account;
+        }
+
+        [Concern(typeof (Transaction))]
+        public class when_transferring_funds_from_one_account_to_multiple_accounts : concern
+        {
+            Establish c = () =>
+            {
+                chequing = DetailAccount.New(Currency.CAD);
+                savings = DetailAccount.New(Currency.CAD);
+                rrsp = DetailAccount.New(Currency.CAD);
+                chequing.add(Entry.New<Deposit>(100, Currency.CAD));
+            };
+
+            Because b = () =>
+            {
+                sut.withdraw(chequing, new Quantity(100, Currency.CAD));
+                sut.deposit(savings, new Quantity(75, Currency.CAD));
+                sut.deposit(rrsp, new Quantity(25, Currency.CAD));
+                sut.post();
+            };
+
+            It should_increase_the_balance_of_each_destination_account = () =>
+            {
+                savings.balance().should_be_equal_to(new Quantity(75, Currency.CAD));
+                rrsp.balance().should_be_equal_to(new Quantity(25, Currency.CAD));
+            };
+
+            It should_decrease_the_balance_of_the_source_account = () =>
+            {
+                chequing.balance().should_be_equal_to(new Quantity(0, Currency.CAD));
+            };
+
+            static DetailAccount chequing;
+            static DetailAccount savings;
+            static DetailAccount rrsp;
+        }
+
+        [Concern(typeof (Transaction))]
+        public class when_a_transaction_does_not_reconcile_to_a_zero_balance : concern
+        {
+            Establish c = () =>
+            {
+                chequing = DetailAccount.New(Currency.CAD);
+                savings = DetailAccount.New(Currency.CAD);
+                chequing.add(Entry.New<Deposit>(100, Currency.CAD));
+            };
+
+            Because b = () =>
+            {
+                sut.withdraw(chequing, new Quantity(1, Currency.CAD));
+                sut.deposit(savings, new Quantity(100, Currency.CAD));
+                call = () =>
+                {
+                    sut.post();
+                };
+            };
+
+            It should_not_transfer_any_money = () =>
+            {
+                call.should_have_thrown<TransactionDoesNotBalance>();
+            };
+
+            static DetailAccount chequing;
+            static DetailAccount savings;
+            static Action call;
+        }
+    }
+}
\ No newline at end of file
product/specs/unit/service/domain/hierarchy/HierarchySpecs.cs
@@ -0,0 +1,126 @@
+using gorilla.utility;
+using Machine.Specifications;
+using solidware.financials.service.domain.hierarchy;
+
+namespace specs.unit.service.domain.hierarchy
+{
+    public class HierarchySpecs
+    {
+        public abstract class concern : runner<Hierarchy>
+        {
+            protected override Hierarchy create_sut()
+            {
+                return new Hierarchy();
+            }
+        }
+
+        [Concern(typeof (Hierarchy))]
+        public class when_visiting_item_in_a_hierarchy : concern
+        {
+            Establish c = () =>
+            {
+                visitor = Create.an<Visitor<Hierarchy>>();
+                middle = new Hierarchy();
+                bottom = new Hierarchy();
+            };
+
+            Because b = () =>
+            {
+                top = sut;
+                top.add(middle);
+                middle.add(bottom);
+                top.accept(visitor);
+            };
+
+            It should_visit_everyone = () =>
+            {
+                visitor.received(x => x.visit(top));
+                visitor.received(x => x.visit(top));
+                visitor.received(x => x.visit(middle));
+                visitor.received(x => x.visit(bottom));
+            };
+
+            static Visitor<Hierarchy> visitor;
+            static Hierarchy top;
+            static Hierarchy middle;
+            static Hierarchy bottom;
+        }
+
+        [Concern(typeof (Hierarchy))]
+        public class when_moving_a_sub_tree_from_one_tree_to_another : concern
+        {
+            Establish c = () =>
+            {
+                old_tree = new Hierarchy();
+                new_tree = new Hierarchy();
+                child = new Hierarchy();
+            };
+
+            Because b = () =>
+            {
+                old_tree.add(child);
+                child.move_to(new_tree);
+            };
+
+            It should_remove_the_sub_tree_from_the_old_tree = () =>
+            {
+                old_tree.contains(child).should_be_false();
+                child.belongs_to(old_tree).should_be_false();
+            };
+
+            It should_add_the_sub_tree_to_the_new_one = () =>
+            {
+                new_tree.contains(child).should_be_true();
+                child.belongs_to(new_tree).should_be_true();
+            };
+
+            static Hierarchy old_tree;
+            static Hierarchy new_tree;
+            static Hierarchy child;
+        }
+
+        [Concern(typeof (Hierarchy))]
+        public class when_removing_a_descendant_from_a_tree_that_it_does_not_belong_to : concern
+        {
+            Establish c = () =>
+            {
+                orphan = new Hierarchy();
+            };
+
+            Because b = () =>
+            {
+                sut.remove(orphan);
+            };
+
+            It should_ignore_the_descdendant = () =>
+            {
+                sut.contains(orphan).should_be_false();
+                orphan.belongs_to(sut).should_be_false();
+            };
+
+            static Hierarchy orphan;
+        }
+
+        [Concern(typeof (Hierarchy))]
+        public class when_moving_a_child_to_another_tree_but_does_not_already_belong_to_a_tree : concern
+        {
+            Establish c = () =>
+            {
+                orphan = new Hierarchy();
+            };
+
+            Because b = () =>
+            {
+                orphan.move_to(sut);
+            };
+
+            It should_move_the_orphan_to_the_new_family = () =>
+            {
+                sut.contains(orphan).should_be_true();
+                orphan.belongs_to(sut).should_be_true();
+            };
+
+            static Hierarchy orphan;
+        }
+    }
+}
\ No newline at end of file
product/specs/unit/service/domain/payroll/CompensationSpecs.cs
@@ -0,0 +1,53 @@
+using System;
+using Machine.Specifications;
+using solidware.financials.service.domain;
+using solidware.financials.service.domain.payroll;
+
+namespace specs.unit.service.domain.payroll
+{
+    public class CompensationSpecs
+    {
+        public abstract class concern : runner<Compensation>
+        {
+            protected override Compensation create_sut()
+            {
+                return new Compensation();
+            }
+        }
+
+        [Concern(typeof (Compensation))]
+        public class when_calculating_the_amount_unvested : concern
+        {
+            Because b = () =>
+            {
+                //Calendar.stop(() => new DateTime(2009, 06, 07));
+                //sut.increase_salary_to(65500);
+                Calendar.stop(() => new DateTime(2009, 09, 15));
+                sut.issue_grant(4500.00, 10.00, new One<Third>(), new Annually());
+
+                Calendar.start();
+                sut.grant_for(new DateTime(2009, 09, 15)).change_unit_price_to(20.00);
+            };
+
+            It should_indicate_that_nothing_has_vested_before_the_first_anniversary = () =>
+            {
+                sut.unvested_balance(new DateTime(2010, 09, 14)).should_be_equal_to(9000);
+            };
+
+            It should_indicate_that_one_third_has_vested_after_the_first_anniversary = () =>
+            {
+                sut.unvested_balance(new DateTime(2010, 09, 15)).should_be_equal_to(6000);
+            };
+
+            It should_indicate_that_two_thirds_has_vested_after_the_second_anniversary = () =>
+            {
+                sut.unvested_balance(new DateTime(2011, 09, 15)).should_be_equal_to(3000);
+            };
+
+            It should_indicate_that_the_complete_grant_has_vested_after_the_third_anniversary = () =>
+            {
+                sut.unvested_balance(new DateTime(2012, 09, 15)).should_be_equal_to(0);
+            };
+        }
+    }
+}
\ No newline at end of file
product/specs/unit/service/domain/payroll/DateSpecs.cs
@@ -0,0 +1,25 @@
+using System;
+using Machine.Specifications;
+using solidware.financials.service.domain;
+
+namespace specs.unit.service.domain.payroll
+{
+    public class DateSpecs
+    {
+        public abstract class concern : runner<Date> {}
+
+        [Concern(typeof (Date))]
+        public class when_two_dates_are_the_same : concern
+        {
+            It should_be_equal = () =>
+            {
+                sut.Equals(new DateTime(2009, 01, 01, 09, 00, 01)).should_be_true();
+            };
+
+            protected override Date create_sut()
+            {
+                return new DateTime(2009, 01, 01, 01, 00, 00);
+            }
+        }
+    }
+}
\ No newline at end of file
product/specs/unit/service/domain/payroll/GrantSpecs.cs
@@ -0,0 +1,57 @@
+using System;
+using Machine.Specifications;
+using solidware.financials.service.domain;
+using solidware.financials.service.domain.payroll;
+
+namespace specs.unit.service.domain.payroll
+{
+    public class GrantSpecs
+    {
+        public abstract class concern : runner<Grant>
+        {
+            protected override Grant create_sut()
+            {
+                Calendar.stop(() => new DateTime(2010, 01, 01));
+                return Grant.New(120, 10, new One<Twelfth>(), new Monthly());
+            }
+        }
+
+        [Concern(typeof (Grant))]
+        public class when_checking_what_the_outstanding_balance_of_a_grant_is : concern
+        {
+            It should_return_the_full_balance_before_the_first_vesting_date = () =>
+            {
+                Calendar.stop(() => new DateTime(2010, 01, 31));
+                sut.balance().should_be_equal_to(120);
+            };
+
+            It should_return_the_unvested_portion_after_the_first_vesting_date = () =>
+            {
+                Calendar.stop(() => new DateTime(2010, 02, 01));
+                sut.balance().should_be_equal_to(110);
+            };
+        }
+
+        [Concern(typeof (Grant))]
+        public class when_checking_what_the_value_of_a_grant_was_in_the_past : concern
+        {
+            Because b = () =>
+            {
+                Calendar.stop(() => january_15);
+                sut.change_unit_price_to(20);
+
+                Calendar.reset();
+                sut.change_unit_price_to(40);
+                result = sut.balance(january_15);
+            };
+
+            It should_return_the_correct_amount = () =>
+            {
+                result.should_be_equal_to(240);
+            };
+
+            static Money result;
+            static Date january_15 = new DateTime(2010, 01, 15);
+        }
+    }
+}
\ No newline at end of file
product/specs/unit/service/domain/payroll/MoneySpecs.cs
@@ -0,0 +1,24 @@
+using Machine.Specifications;
+using solidware.financials.service.domain.payroll;
+
+namespace specs.unit.service.domain.payroll
+{
+    public class MoneySpecs
+    {
+        public abstract class concern : runner<Money> {}
+
+        [Concern(typeof (Money))]
+        public class when_two_monies_are_the_same : concern
+        {
+            It should_be_equal = () =>
+            {
+                sut.Equals(100.00);
+            };
+
+            protected override Money create_sut()
+            {
+                return 100.00;
+            }
+        }
+    }
+}
\ No newline at end of file
product/specs/unit/service/domain/property_bag/PropertyBagSpecs.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Machine.Specifications;
+using solidware.financials.service.domain.property_bag;
+
+namespace specs.unit.service.domain.property_bag
+{
+    public class PropertyBagSpecs
+    {
+        public abstract class concern : runner<PropertyBag>
+        {
+            Establish c = () => Console.Out.WriteLine("init concern");
+
+            protected override PropertyBag create_sut()
+            {
+                return Bag.For<TargetType>();
+            }
+        }
+
+        [Concern(typeof (PropertyBag))]
+        public class when_creating_a_property_bag_from_a_known_type : concern
+        {
+
+            It should_include_each_property_from_the_target_type = () =>
+            {
+                sut.property_named("name").should_not_be_null();
+            };
+
+            Establish c = () => { Console.Out.WriteLine("blah");};
+
+            It should_not_contain_properties_that_are_not_on_the_target_type = () =>
+            {
+                sut.property_named("blah").should_be_an_instance_of<UnknownProperty>();
+            };
+        }
+
+        [Concern(typeof (PropertyBag))]
+        public class when_iterating_through_each_property_in_the_bag : concern
+        {
+            Establish c = () => Console.Out.WriteLine("init when_iterating_through_each_property_in_the_bag");
+
+            It should_contain_a_reference_for_each_property_on_the_target_type = () =>
+            {
+                results.Count().should_be_equal_to(1);
+            };
+
+            Because b = () =>
+            {
+                results = sut.all();
+            };
+
+            static IEnumerable<Property> results;
+        }
+    }
+
+    public class TargetType
+    {
+        public string name { get; set; }
+    }
+}
\ No newline at end of file
product/specs/unit/service/domain/DateSpecs.cs
@@ -0,0 +1,57 @@
+using System;
+using Machine.Specifications;
+using solidware.financials.service.domain;
+
+namespace specs.unit.service.domain
+{
+    public class DateSpecs
+    {
+        public abstract class concern  {}
+
+        [Concern(typeof (Date))]
+        public class when_checking_if_a_date_is_before_another : concern
+        {
+            Establish c = () =>
+            {
+                today = DateTime.Now;
+                tomorrow = today.plus_days(1);
+            };
+
+            It should_return_true_when_it_is = () =>
+            {
+                today.is_before(tomorrow).should_be_true();
+            };
+
+            It should_return_false_when_it_is_not = () =>
+            {
+                tomorrow.is_before(today).should_be_false();
+            };
+
+            static Date today;
+            static Date tomorrow;
+        }
+
+        [Concern(typeof (Date))]
+        public class when_checking_if_a_date_is_after_another : concern
+        {
+            Establish c = () =>
+            {
+                today = DateTime.Now;
+                tomorrow = today.plus_days(1);
+            };
+
+            It should_return_true_when_it_is = () =>
+            {
+                tomorrow.is_after(today).should_be_true();
+            };
+
+            It should_return_false_when_it_is_not = () =>
+            {
+                today.is_after(tomorrow).should_be_false();
+            };
+
+            static Date today;
+            static Date tomorrow;
+        }
+    }
+}
\ No newline at end of file
product/specs/unit/service/domain/runner.cs
@@ -0,0 +1,14 @@
+using Machine.Specifications;
+
+namespace specs.unit.service.domain
+{
+    public class runner<T>
+    {
+        static protected T sut;
+
+        protected virtual T create_sut()
+        {
+            return default(T);
+        }
+    }
+}
\ No newline at end of file
product/specs/Assertions.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Machine.Specifications;
+
+namespace specs
+{
+    static public class Assertions
+    {
+        static public void should_be_equal_to<T>(this T item_to_validate, T expected_value)
+        {
+            item_to_validate.ShouldEqual(expected_value);
+        }
+
+        static public void should_be_the_same_instance_as<T>(this T left, T right)
+        {
+            ReferenceEquals(left, right).ShouldBeTrue();
+        }
+
+        static public void should_be_null<T>(this T item)
+        {
+            item.ShouldBeNull();
+        }
+
+        static public void should_not_be_null<T>(this T item) where T : class
+        {
+            item.ShouldNotBeNull();
+        }
+
+        static public void should_be_greater_than<T>(this T actual, T expected) where T : IComparable
+        {
+            actual.ShouldBeGreaterThan(expected);
+        }
+
+        static public void should_be_less_than(this int actual, int expected)
+        {
+            actual.ShouldBeLessThan(expected);
+        }
+
+        static public void should_contain<T>(this IEnumerable<T> items_to_peek_in_to, T items_to_look_for)
+        {
+            items_to_peek_in_to.Contains(items_to_look_for).should_be_true();
+        }
+
+        static public void should_not_contain<T>(this IEnumerable<T> items_to_peek_into, T item_to_look_for)
+        {
+            items_to_peek_into.Contains(item_to_look_for).should_be_false();
+        }
+
+        static public void should_be_an_instance_of<T>(this object item)
+        {
+            item.should_be_an_instance_of(typeof (T));
+        }
+
+        static public void should_be_an_instance_of(this object item, Type type)
+        {
+            item.ShouldBe(type);
+        }
+
+        static public void should_have_thrown<TheException>(this Action action) where TheException : Exception
+        {
+            typeof (TheException).ShouldBeThrownBy(action);
+        }
+
+        static public void should_be_true(this bool item)
+        {
+            item.ShouldBeTrue();
+        }
+
+        static public void should_be_false(this bool item)
+        {
+            item.ShouldBeFalse();
+        }
+
+        static public void should_contain<T>(this IEnumerable<T> items, params T[] items_to_find)
+        {
+            foreach (var item_to_find in items_to_find)
+            {
+                items.should_contain(item_to_find);
+            }
+        }
+
+        static public void should_only_contain<T>(this IEnumerable<T> items, params T[] itemsToFind)
+        {
+            items.Count().should_be_equal_to(itemsToFind.Length);
+            items.should_contain(itemsToFind);
+        }
+
+        static public void should_be_equal_ignoring_case(this string item, string other)
+        {
+            item.ShouldBeEqualIgnoringCase(other);
+        }
+    }
+}
\ No newline at end of file
product/specs/ConcernAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using Machine.Specifications;
+
+namespace specs
+{
+    public class ConcernAttribute : SubjectAttribute
+    {
+        public ConcernAttribute(Type subjectType) : base(subjectType) {}
+        public ConcernAttribute(Type subjectType, string subject) : base(subjectType, subject) {}
+        public ConcernAttribute(string subject) : base(subject) {}
+    }
+}
\ No newline at end of file
product/specs/specs.csproj
@@ -36,7 +36,9 @@
     <Reference Include="Castle.Core">
       <HintPath>..\..\thirdparty\castle\Castle.Core.dll</HintPath>
     </Reference>
-    <Reference Include="Db4objects.Db4o, Version=8.0.184.15484, Culture=neutral, PublicKeyToken=6199cd4f203aa8eb, processorArchitecture=MSIL" />
+    <Reference Include="Db4objects.Db4o">
+      <HintPath>..\..\thirdparty\db4o\Db4objects.Db4o.dll</HintPath>
+    </Reference>
     <Reference Include="gorilla.infrastructure, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL" />
     <Reference Include="gorilla.utility, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
@@ -59,10 +61,26 @@
     <Reference Include="WindowsBase" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Assertions.cs" />
+    <Compile Include="ConcernAttribute.cs" />
     <Compile Include="Mocking.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Create.cs" />
     <Compile Include="unit\infrastructure\ProxyFactorySpecs.cs" />
+    <Compile Include="unit\service\domain\accounting\BOEDSpecs.cs" />
+    <Compile Include="unit\service\domain\accounting\CurrencySpecs.cs" />
+    <Compile Include="unit\service\domain\accounting\DetailAccountSpecs.cs" />
+    <Compile Include="unit\service\domain\accounting\QuantitySpecs.cs" />
+    <Compile Include="unit\service\domain\runner.cs" />
+    <Compile Include="unit\service\domain\accounting\SummaryAccountSpecs.cs" />
+    <Compile Include="unit\service\domain\accounting\TransactionSpecs.cs" />
+    <Compile Include="unit\service\domain\DateSpecs.cs" />
+    <Compile Include="unit\service\domain\hierarchy\HierarchySpecs.cs" />
+    <Compile Include="unit\service\domain\payroll\CompensationSpecs.cs" />
+    <Compile Include="unit\service\domain\payroll\DateSpecs.cs" />
+    <Compile Include="unit\service\domain\payroll\GrantSpecs.cs" />
+    <Compile Include="unit\service\domain\payroll\MoneySpecs.cs" />
+    <Compile Include="unit\service\domain\property_bag\PropertyBagSpecs.cs" />
     <Compile Include="unit\service\handlers\AddIncomeCommandMessageHandlerSpecs.cs" />
     <Compile Include="unit\service\orm\DB4OUnitOfWorkFactorySpecs.cs" />
     <Compile Include="unit\service\orm\DB4OUnitOfWorkSpecs.cs" />