Difference Adjustment

Adjust an erroneous event with entries which reflect the different between what was recorded and what should have been recorded.

02 January 2005

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.

If you can't edit the entries when you find a mistake, you need to make new entries. Reversal Adjustment is a simple way to do this, but it results in a lot of entries. For each original entry you make two more: a reversing entry and the replacement entry.

With Difference Adjustment you make the adjustment with just one entry that contains the difference between the original entry and what that entry should have been.

Indeed you can often fix several erroneous entries with one adjusting entry, as Figure 0.25 suggests. Not just does this cut down on the amount of entries you have to create, it also can make things clearer. You can easily see the difference caused by a particular adjustment, rather than having to work your way through the reversals and replacements.

Figure 1: Adjusting multiple events with one entry.

How it Works

The complication of this approach comes as you try to figure out how to caculate these adjusting entries. One approach is to write specifc code to calculate adjustments. The downside of this is that it's difficult to do this without duplicating a lot of the regular calculation code.

An alternative which I've seen work rather well, is to use a Parallel Model. Inside the Parallel Model use Reversal Adjustment to figure out what the correct entries should have been. Then compare the balances of the accounts in the Parallel Model with those in the real accounts and post the differences as adjustments.

That's the trite summary, here are the gory details. We begin with a customer with a usage account containing the entries that we now know are erroneous. These were made over the last few months, we are processing the adjustment on Nov 20.

The first step is to create a set of shadow accounts. Essentially this means taking a copy of the usage account.

Now the new events are processed using Reversal Adjustments to the shadow accounts.

Then we compare the balance of the shadow and real account and post an entry for the difference.

When to Use It

I tend to see this pattern used when Account is being used. This is because you need to figure out which entries from the replacement match which original entries so you can compute the difference between the two. To match entries you need to get entries with the same discriminators. This is easiest when you have an account, as that way there is only one discriminator. The value of the adjusting entries is just the difference between the balance of the actual and shadow account for each account.

Even so the choice between Reversal Adjustment and Difference Adjustment is not simple, and usually depends on how the domain experts want to think about how the adjustments are carried out. If they want to see explicit cancelling, use Reversal Adjustment, if they prefer a summary use Difference Adjustment. Fortunately refactoring from Reversal Adjustment to Difference Adjustment is not too difficult. The reverse refactoring is also possible, although reconstructing the reversal data is somewhere between involved and impossible.

Example: Adjusting Single Electricity Usage (Java)

Here we'll take the electricity usage example again and show how to do this with difference adjustment. Here's the basic setup code.

class ExampleTester...

  public void setUp() {
      MfDate.setToday(2004, 4, 1);
      watson = sampleCustomer();
      original = new Usage(Unit.KWH.amount(50),
                           new MfDate(2004, 3, 31),
                           watson);
      eventList.add(original);
      eventList.process();
      MfDate.setToday(2004, 6, 1);
      replacement = new Usage(Unit.KWH.amount(70),
                              new MfDate(2004, 3, 31),
                              watson);
      adjustment = new DifferenceAdjustment(replacement, original);
      eventList.add(adjustment);
      eventList.process();
  }

class DifferenceAdjustment…

  public DifferenceAdjustment(MfDate whenOccurred,
                              Subject subject,
                              List<AccountingEvent> oldEvents,
                              List<AccountingEvent> newEvents)
  {
      super(whenOccurred, subject);
      this.oldEvents = oldEvents;
      this.newEvents = newEvents;
  }

This same example code works for adjusting multiple events as well, but naturally the setup code is somewhat longer.

The logic for doing the difference adjustment is in the process method for the adjustment event.

class DifferenceAdjustment…

  public void process() {
      assert !isProcessed;
      adjust();
      markProcessed();
  }
  void adjust() {
      getCustomer().beginAdjustment();
      reverseOldEvents();
      processReplacements();
      getCustomer().commitAdjustment(this);
      recordSecondaryEvents();
  }

The basic logic for the adjustment is to build the shadow accounts, carry out the processing with Reversal Adjustments in the shadow accounts and then post the Difference Adjustment.

The first step is making the shadow accounts.

class Customer...

  public void beginAdjustment() {
      assert ! isAdjusting();
      savedRealAccounts = accounts;
      accounts = copyAccounts(savedRealAccounts);
  }
  private boolean isAdjusting() {
      return null != savedRealAccounts;
  }
  public Map<AccountType, Account> copyAccounts(Map<AccountType, Account> from) {
       Map<AccountType, Account> result = new HashMap<AccountType, Account>();
       for (AccountType t : from.keySet()) result.put(t, from.get(t).copy());
       return result;
   }

I've made the customer is responsible for shadow creation (and later posting the adjustments). That might be an innappropriate responsibility for the customer, but having the adjustment copy the accounts reveals too much about the internals of the customer. This is one of those decisions that depends on the particular way a system is handling its responsibilities, so there's little I can say in general on this. You have to choose the right place where the knowledge of the shadowing resides depending on your own circumstances.

Now we're operating in the Parallel Model we can use Reversal Adjustment by reversing all the entries of the original event and processing a replacement.

class uses...

  void reverseOldEvents() {
      for (AccountingEvent e : oldEvents)
          e.reverse();
  }
  void processReplacements() {
      for (AccountingEvent e : newEvents) e.process();
  }

class AccountingEvent...

  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);
   }

The target account here for the reversal isn't the same account as the account the entry is in, because we are referring to the real entries (which we reached via the original event) not the shadow entries. The reversing entries however need to be made in the shadow account.

Once we done the posting of the reversals the customer can now post the Difference Adjustment into the real account.

class Customer...

  public void commitAdjustment(Adjustment adjustment) {
      assert isAdjusting();
      for (AccountType t : AccountType.values())
           adjustAccount(t, adjustment);
      endShadowAccounts();
  }
  public void adjustAccount(AccountType type, Adjustment adjustment) {
       Account correctedAccount = accounts.get(type);
       Account originalAccount = savedRealAccounts.get(type);
       Money difference = correctedAccount.balance().subtract(originalAccount.balance());
       Entry result = new Entry(difference, MfDate.today());
       originalAccount.post(result);
       adjustment.addResultingEntry(result);
   }
 public void endShadowAccounts() {
      assert isAdjusting();
      accounts = savedRealAccounts;
      savedRealAccounts = null;
  }