Reversal Adjustment

02 January 2006

This is part of the Further Enterprise Application Architecture development writing that I was doing in the mid 2000’s. Sadly too many other things have claimed my attention since, so I haven’t had time to work on them further, nor do I see much time in the foreseeable future. As such this material is very much in draft form and I won’t be doing any corrections or updates until I’m able to find time to work on it again.

How it Works

For each entry that needs to be adjusted you create two new entries. One entry is a simple reversal of the previous entry, using the same occurred date and the same amount with opposite sign. Then you post a new entry which is what the old amount should have been.

So let's say we have a record of 50kwh hours of electricity usage in March. This event was originally recorded on 5th April and processed on the 10th of April. This yields the objects in Figure 1

Figure 1: Events before adjustment.

Then on 1st June we realize that a mistake was made and the amount should have been 80 kwh. This results in the structure of Figure 2.

You might be suprised that the reversing entry appears on the old event and not the new one. This is because the new event may itself get adjusted later, and in that case we don't want to produce entries that reverse the reversing entries. So it makes sense instead to put them on the original event. We also mark that event as adjusted to make it clear that it has been adjusted and so shouldn't be adjusted again.

Figure 2: Events after Adjustment

Using Reversal Adjustment results in a lot of entries in these reversal pairs. So this means that if anyone wants to see a list of entries, they'll often want to see the entries with all the reversal pairs excluded. So you'll need to provide a query method that filters for this information. Since a lot of data can be involved this will usually affect your database queries as well.

When to Use It

Reversal Adjustment is a simple alternative when entries are immutable and thus you can't use Replacement Adjustment. The main disadvantage is that it results in a lot of entries: once you're done you have three entres for every one you had before. Do this a few times and you end up with a lot of entries, many in reversed pairs. While you can filter this, they are still a pain to have around.

Difference Adjustment is the principal alternative. Usually the choice between them will depend on how your users want to see the information. If the entries are mutable then it's best to use Replacement Adjustment. Often you'll find entries are mutable up to a certain date, and immutable after that. This will lead you to a conbination of Reversal Adjustment and Replacement Adjustment.

Example: Reversing incorrect electricity usage (Java)

Here's our example of an incorrect electricity usage and how it should be reversed.

public void setUp() {
    MfDate.setToday(2004,4,1);
    watson = new Customer("Dr Watson");
    watson.setServiceAgreement(testAgreement());
    usageEvent = new Usage(Unit.KWH.amount(50),
                 new MfDate(2004, 3, 31),
                 watson);
    eventList.add(usageEvent);
    eventList.process();
    MfDate.setToday(2004,6,1);
    replacement = new Usage(Unit.KWH.amount(70),
                    new MfDate(2004, 3, 31),
                    watson,
                    usageEvent);
    eventList.add(replacement);
    eventList.process();
}

In this implementation we provide a constructor for our accounting event that includes a slot for an adjusted event.

class AccountingEvent...

  public AccountingEvent(EventType type, MfDate whenOccurred, Subject subject, AccountingEvent adjustedEvent) {
      this.type = type;
      this.whenOccurred = whenOccurred;
      this.whenNoticed = MfDate.today();
      this.subject = subject;
      this.adjustedEvent = adjustedEvent;
      adjustedEvent.setReplacementEvent(this);
  }
private AccountingEvent adjustedEvent;

During processing, if there's an adjusted event present, it gets reversed first. Reversing just involves posting an opposite entry for each original entry. The rest of the event processing can then just be done as normal.

class AccountingEvent...

  public void process() {
      assert !isProcessed;
      if (adjustedEvent != null) adjustedEvent.reverse();
      subject.process(this);
      markProcessed();
  }
public void reverse() {
    assert isProcessed();
    for (Entry e : getResultingEntries()) reverseEntry(e);
    for(AccountingEvent ev : getSecondaryEvents()) ev.reverse();
}

public void reverseEntry(Entry arg) {
    Entry reversingEntry = new Entry(arg.getAmount().negate(), arg.getDate());
    Account targetAccount = subject.accountFor(arg.getAccount().type());
    targetAccount.post(reversingEntry);
 }