<<

. 2
( 2)



UK P QK V E C U P C T V F G I I G N K VN W O C G E P K U F T C Y M Y C G T Q O V K D F Q Q I C U K G U C E F G I I G N K V N W O G J 6
FGIIG. KVNW/ GFQ% GNROC5
}
assertEquals(Money.dollars(-700), revenue.balance());
assertEquals(Money.dollars(200), deferred.balance());
assertEquals(Money.dollars(500), receivables.balance());
revenue.withdraw(Money.dollars(200), deferred, new MfDate(1,4,99));
revenue.withdraw(Money.dollars(500), receivables, new MfDate(1,4,99));
receivables = new Account(Currency.USD);
deferred = new Account(Currency.USD);
revenue = new Account(Currency.USD);
public void testBalanceUsingTransactions() {
 J V K Y M T Q Y Q V T G K U C G V Q N C U P Q K V C N W RK P C O T Q H G F Q E G J V U G M C O U K J 6
}
new AccountingTransaction (amount, this, target, date);
void withdraw(Money amount, Account target, MfDate date) {
 VE GL D Q V PW QEE C GJ V P Q F QJ VG O G N D CV KWU C G FK X QTR
QV GUPGU UGMC O VK [NVEGTKF TQVEWTVUPQE PQKVECUPCTV IPKVPWQEEC IPKUW PCJV TGJVC4
P QK VP GX P QE I PKF QE F P C T Q VEW TV UP QE G J V T QH UU GEE C GI CME C R H Q PQ KV C PKD
O Q E C U K U K J V C X C , P +  U P Q K V E C U P C T V H Q U U G E Q T R G J V P K P C J V T G J V Q U G K T V P G G V C G T E V• P C E W Q [
V C J V Q U F GV EK T V U G T G D Q V [ T V P G T Q H T Q V E W TV U P Q E G J V G M C O Q V F G G P V U WL W Q [ U K J V J VK 9
}
entries.add(toEntry);
to.addEntry(toEntry);
Entry toEntry = new Entry (amount, date);
entries.add(fromEntry);
from.addEntry(fromEntry);
Entry fromEntry = new Entry (amount.negate(), date);
public AccountingTransaction(Money amount, Account from, Account to, MfDate date) {
private Collection entries = new HashSet();
public class AccountingTransaction {
V E G L D Q P QK V E C U P C TV I PK V P W Q E E C G N R OK U C U F G G P V U W L [N N C G T G U C E F G I I G N Q Y V G J V F G G F P+
GU C E F GII GN Q Y V T GN R OKU G JV
J V K Y I PK V T C V U U G U C E F G I I G N K VN W O F P C F G I I G N Q Y V G J V J V Q D T Q H G F Q E G N R O C U W Q [ G X K I N N• +
FGIIG. QY6 GFQ% GNROC5

 P Q K V E C U P C T V G J V V U Q R P G J V P C E + P Q K V E C U P C T V G J V Q V U G K T V P G F G F F C G X• + G E P 1
}
this.transaction = transaction;
this.account = account;
this.date = date;
this.amount = amount;
// only used by AccountingTransaction
Entry(Money amount, MfDate date, Account account, AccountingTransaction transaction) {
private AccountingTransaction transaction;
private Account account;
private MfDate date;
private Money amount;
class Entry...
UMP K N [ C Y QY V GJ V IP KP KC VP K C O J V KY N CG F Q V T GKU C G J EW O V K U GMC O J EK J Y
GN D C V W O O K G T C U G K T V P '  V P W Q E E C G JV F P C P QK V E C U P C TV G J V J V Q D F P C [ T V P G G J V P G G Y V G D
U P Q K V CK E Q U U C N C P QK V E G TK F K D G X T G U G T R Q V U U CN E [ T V P G V P G T G H H K F C I P K U W O• + G U C E UK J V P +
}
entries.add(new Entry (amount, date, account, this));
("cannot add entry to a transaction that's already posted");
if (wasPosted) throw new ImmutableTransactionException
public void add (Money amount, Account account) {
class Transaction...
 F G VU Q R P G G D [ F C GT N C
V • P U C J P Q K V E C U P C T V G J V I P K F K X Q T R P QK V E C U P C T V G J V Q V U G K T V P G U F F C F Q J V G O F F C G J 6
 PK CN R Z G Q V T G N R O K U F C V C U I PK JV U G M C O VK V W D M P C D J U KV K T $ F N Q [ O G N F
P C J V • P F N W Q Y U K J 6  P QK V E C U P C T V G N Q J Y G J V T Q H G V C F G P Q G X C J + G N R O C Z G U K J V J V K Y Q 5
}
this.date = date;
public AccountingTransaction(MfDate date) {
private boolean wasPosted = false;
private Collection entries = new HashSet();
private MfDate date;
public class AccountingTransaction {
 T Q V E W T V U P Q E F P C U F N G K H G J V J VK Y I P K V T C V U G F Q E G J V N C G X G T N N• +




class AccountingTransaction...
public void post() {
if (!canPost())
throw new UnableToPostException();
Iterator it = entries.iterator();
while (it.hasNext()) {
Entry each = (Entry) it.next();
each.post();
}
wasPosted = true;
}
public boolean canPost(){
return balance().isZero();
}
private Money balance() {
if (entries.isEmpty()) return Money.dollars(0);
Iterator it = entries.iterator();
Entry firstEntry = (Entry) it.next();
Money result = firstEntry.amount();
while (it.hasNext()) {
Entry each = (Entry) it.next();
result = result.add(each.amount());
}
return result;
}
class Entry...
void post() {
// only used by AccountingTransaction
account.addEntry(this);
}
 UK J V G M K N G F Q E J V K Y P Q K V E C U P C T V G J V G U W P G J V P C E +
AccountingTransaction multi = new AccountingTransaction(new MfDate(2000,1,4));
multi.add(Money.dollars(-700), revenue);
multi.add(Money.dollars(500), receivables);
multi.add(Money.dollars(200), deferred);
multi.post();
assertEquals(Money.dollars(500), receivables.balance());
assertEquals(Money.dollars(200), deferred.balance());
assertEquals(Money.dollars(-700), revenue.balance());

F G I I G N K VN W O C G U W Q V F T C Y M Y C Q U U• VK [ J Y Q V U V PK Q R U U G PK U W D V U Q R F P C R W V G U U K J V N N #
H Q G O Q U U P QK V E C U P C TV F G I I G N Q Y V F G G P [ N P Q W Q [ HK V C J V U K U Y G P F Q Q I G J 6  P QK V E C U P C TV
 P QK V E C U P C T V F G I I G N K VN W O C J VK Y G E C H T G V P K F G I I G N Q Y V G J V V P G O G N R O K P C E W Q [ G O K V G J V



class Account...
void withdraw(Money amount, Account target, MfDate date) {
AccountingTransaction trans = new AccountingTransaction(date);
trans.add(amount.negate(), this);
trans.add(amount, target);
trans.post();
}
FGVCGTE [NNCKEKHHQ U•VK GTQHGD VEGLDQ PC HQ UNKCVGF GJV FTQEG4
VEGLD1 FGUQRQT2
[EPGTTWE GXKVCP TKGJV PK UGKEPGTTWE VPGTGHHKF HQ UGKPQO UWQKTCX UFNQ*
IC$[GPQ/
 G V C F C P G X K I G W N C X G N I P K U C U P T W V G T V C J V P Q K V E GN N Q E #
[VTGRQT2 NCTQROG6
GXKVEGHHG UK VEGLDQ GJV PGJY GVCEKFPK QV VEGLDQ GJV JVKY GIPCT GVCF C FTQEG4
FQKTG2 [VKXKVEGHH'
U V P W Q E E C F P C U GN W T I PK V U Q R H Q G T W V E W T V U J R C T I #
MTQYVG0 GNW4 IPKVUQ2

   GT W IK( PK U V E GLD Q GJ V U FN GK [
UK J 6 N K T R # H Q J V   G J V P Q F G U U G E Q T R F P C N K T R # J V  P Q F G F T Q E G T [N N C P K IK T Q U C Y V P G X G
UK J 6  J E T C / PK G I C U W [ V K EK T V E G N G H Q U T W Q J J Y M   H Q F T Q E G T C G X C J G Y [ C U U• V G N Q 5
UG KT VP G NC OT QP T Q H Q F WQ [ VC J V [ C Y G O CU G JV UK J V G V C NW E N CE P CE W Q[ P G G D G XC J F N WQ J U
V P W Q O C Y G P G J V V C J Y U K J EK J Y [ T V P G Y G P C V U Q R W Q [ P G J 6  P I K U G V K U Q R R Q J V K Y V P W Q O C
G O C U G J V F P C G V C F F G T T W E E Q G O C U G JV I P K U W [ TV P G U W QK X G T R G JV H Q N C U T G X G T G N R OK U
C UK [T VP G G P 1 U GKT V P G Y GP Q Y V G V CGT E W Q [ FG VU WL F C GD Q V U FGG P V C J V [T V PG JE CG T Q(
MTQY VK IPKMC/
 UK JV G NFP C J Q V [ CY G NR O KU C UV PG UGT RGT N CUT GXG 4  VE GHH G VG P G OC U G J V G XC J
N NK Y V C J V U GK T V P G Y G P G V C G T E Q V F G G P W Q [ P G J V I PK V U WL F C P G J Y U GK T V P G V K F G V• P C E W Q [ H +
resulting entries
amount = $600 amount = 60 kwh
replacing Usage Entry new Usage Event
replacement event
amount = ($500)
reversing Usage Entry
adjusted event
amount = $500 amount = 50 kwh
original Usage Entry resulting entries original Usage Event
 UGKT VPG
VPGOGECNRGT VEGTTQE IPKVCNWENCE PGJV FPC UGKTVPG IPKUTGXGT IPKVUQR [D UGKTVPG IPKVKUKZG VUWLF#
4GXGTUCN #.LWUVOGPV

V P G O V U WL F C G J V T G V H C U V E G L D 1     G T W I K (
has been adjusted = false
resulting entries when noticed = 15 Oct 99
date = 15 Oct 99
when occurred = 1 Oct 99
amount = $600
amount = 60 kwh
replacing Usage Entry
new Usage Event
date = 15 Oct 99 replacement event
amount = ($500)
reversing Usage Entry adjusted event
has been adjusted = true
when noticed = 5 Oct 99
date = 5 Oct 99 when occurred = 1 Oct 99
amount = $500 amount = 50 kwh
resulting entries
original Usage Entry original Usage Event
 P K C I C F G V U W L F C G D V• P F N W Q J U Q U F P C F G V U W L F C P G G D U C J V K V C J V T C G N E V K G M C O Q V
F G V U W L F C U C V P G X G V C J V M T C O Q UN C G 9  V P G X G N C PK I K T Q G JV P Q O G J V V W R QV F C G V U P K G U P G U
U G M C O VK Q 5  U GK T V P G I PK U T G X G T G J V G U T G X G T V C J V U GK T V P G G E W F Q T R Q V V P C Y V • P Q F G Y G U C E
V C J V PK F P C T G V CN F G V U WL F C V G I H N G UV K [ C O V P G X G Y G P G J V G U W C E G D U K U K J 6  G P Q Y G P G JV
V Q P F P C V P G X G FN Q G J V P Q U T C G R R C [ TV P G I PK U T G X G T G J V V C J V F G U K T R W U G D V J I K O W Q ;
     G T W IK ( H Q G T W V E W T V U G J V PK UV N W U G T UK J 6  J Y M   P G G D
GXCJ FNWQJU VPWQ OC GJV FPC GFC O UCY GMCVUKO C VCJV G\KNCGT GY GPW, VU PQ PGJ6
V P G O V U WL F C G T Q H G D U V P G X '     G T W I K (
when noticed = 5 Oct 99
date = 5 Oct 99 when occurred = 1 Oct 99
amount = $500 amount = 50 kwh
a Usage Entry a Usage Event

   T G D Q V E Q P Q J Y M   T QH G I C U W Y G P C J VK Y VK V U WL F C P G J V F P C  T G D Q V E Q
P Q J Y M   T QH G I C U W N C PK I K T Q P C G V C G T E G Y G F Q E V U G V T W Q PK Q 5  F G V UK Z G T G X G P V P G X G
F N Q G J V V C J V T C G R R C V K G M C O U G E P C N C D [ P C V C J V Q U V P G X G FN Q G J V V U WL F C Q V V P G X G Y G P G J V
V P C Y G 9  GN R OK U [N G X KV R G E G F U K V P G O V U W L F C T QH F G G P G Y V C JV T Q K X C J G D N C P T G V Z G G J 6
   G N W 4 I P K V U Q 2 T Q H G F Q E G N R O C U G J V P Q F G U C D U K G F Q E GN R O C U U K J 6
GFQ% GNROC5
  VPGOVUWLF# NCUTGXG4 FPC  VPGOVUWLF# VPGOGECNRG4 HQ PQKVCP
K D O Q E C QV W Q [ F C GN N N K Y UK J 6  V C J V T G VH C GN D C V W O O K F P C G V C F P K C V T G E C QV R W G N D CV W O
G T C U G K T V P G F P K H N N• W Q [ P G V H 1   VPGOVUWLF# VPGOGECNRG4 GUW QV VUGD U•VK PGJV GNDCVWO
GT C U G KTV P G G JV H+  P Q KV C OT QH P K G J V G GU Q V V P C Y UT GU W T W Q [ Y Q J P Q F P G R G F N N K Y O G J V
P G G YV G D G EK Q J E G JV [N N C W U 7  G X KV C P T G VN C N C RK E P K T R G J V U K   V P G O V U WL F # G E P G T G H H K &
 F P W Q T C G X C J Q V P K C R C N N K V U P G V H Q G T C [ G J V G U G J V T GV NK H P C E W Q [
G NK J 9  U T K C R F G U T G X G T P K [ P C O U G K TV P G H Q V Q N C J V K Y R W F P G W Q [ F P C U G O K V Y G H C U K J V Q &
 G T Q H G D F C J W Q [ G P Q [ T G X G T Q H U GK T V P G G G T J V G X C J W Q [ G P Q F G T• W Q [ G E P Q  U G K T V P G H Q V Q N
C PK V N WUGT [G JV V C J V UK GI C V P CXF CU KF P KC O T KGJ 6   VPGOVUWLF# VPGOGECNRG4 GUW V•PCE
W Q [ U W JV F P C GN D C V W O O K G T C U GK T V P G P G J Y G XK V C P T GV N C G N R O K U V U Q O G JV G T C U N C U T G X G 4
VK GUW QV PGJ9
 N N G Y U C U G K T G W S G U C D C V C F T W Q [ V E G HH C
[ N N C W U W N N K Y U K J V F G X N Q X PK G D P C E C V C F H Q V QN C G E PK 5  P QK V C O T Q H P K U K J V T QH U T G VN KH V C J V
F Q J V G O [ T G W S C G F K X Q T R Q V F G G P NN• W Q [ Q 5  F G F WN E Z G U TK C R N C U T G X G T G J V N N C J V K Y U GK T V P G
G J V G G U Q V V P C Y P G V H Q N N• [ G J V U G K T V P G H Q V UK N C G G U Q V U V P C Y G P Q [ P C H K V C J V U P C G O U K J V
Q 5  U T K C R N C U T G X G T G U G J V P K U G K T V P G H Q V Q N C PK U V N W U G T   V P G O V U W L F # N C U T G X G 4 I PK U 7




class Tester...
public void setUp(){
setUpRegular();
setUpLowPay();
usageEvent = new Usage(
Unit.KWH.amount(50),
new MfDate(1999, 10, 1),
new MfDate(1999, 10, 1),
acm);
eventList.add(usageEvent);
eventList.process();
}

public void testAdjustment() {
Usage adjustment1 = new Usage (
Unit.KWH.amount(70),
new MfDate(1999, 10, 1),
new MfDate(1999, 10, 15),
usageEvent);
eventList.add(adjustment1);
eventList.process();
assertEquals(Money.dollars(700), acm.balanceFor(EntryType.BASE_USAGE));
assertEquals(Money.dollars(38.5), acm.balanceFor(EntryType.TAX));
G J V V P G X G T C N W I G T C U C U U C N E G O C U G J V UK V P G X G I P K V U WL F C G JV P QK V C V P G O GN R O K U K J V P +
P Q E C J VK Y F GV C G T E U K V P G X G I PK V U WL F C G J V “ T Q V E W T V U P Q E G JV P K U GK N G E P G T G HH K F [N P Q
 V P G X G F GV U W L F C G J V U G M C V V C J V T QV E W T V U
class AccountingEvent...
private AccountingEvent adjustedEvent, replacementEvent;
AccountingEvent (EventType type, MfDate whenOccurred,
MfDate whenNoticed, AccountingEvent adjustedEvent) {
if (adjustedEvent.hasBeenAdjusted())
throw new IllegalArgumentException (The " + adjustedEvent + " is already adjusted");
this.type = type;
this.whenOccurred = whenOccurred;
this.whenNoticed = whenNoticed;
this.adjustedEvent = adjustedEvent;
adjustedEvent.replacementEvent = this;
}
protected boolean hasBeenAdjusted() {
return (replacementEvent != null);
}
V+  V P G X G V P G O G E C N R G T G J V F P C F G V U WL F C G J V P G G YV G D M PK N G J V R W U V G U T Q V E W TV U P Q E UK J 6
 F G V U WL F C P G G D [ F C G T N C V• P U C J V P G X G F G V U WL F C G J V V C J V U G T W U P G Q U N C
I P K U U G E Q T 2  F G U U G E Q T R UK V P G X G G J V P G J Y U G O Q E J E C Q T R R C G J V Q V G I P C J E F P Q E G U G J 6
 V P G X G F G V U W L F C G J V I PK U T G X G T G F W N E P K Y Q P V U W O V P G X G P C



class AccountingEvent...
public void process() {
Assert.isFalse ("Cannot process an event twice", isProcessed);
if (adjustedEvent != null) adjustedEvent.reverse();
findRule().process(this);
isProcessed = true;
}
void reverse() {
Collection entries = new HashSet(getResultingEntries());
Iterator it = entries.iterator();
while (it.hasNext()) {
Entry each = (Entry) it.next();
Entry reversingEntry = new Entry(
each.getAmount().reverse(),
whenNoticed,
each.getType());
getCustomer().addEntry(reversingEntry);
this.addResultingEntry(reversingEntry);
}
reverseSecondaryEvents();
}
private void reverseSecondaryEvents(){
Iterator it = getSecondaryEvents().iterator();
while (it.hasNext()) {
AccountingEvent each = (AccountingEvent) it.next();
each.reverse();
}
}





secondary event of
replacement event adjusted event an existing Entry
adjusted event

process
[adjusted event != null]
reverse

* get information

* create
reversing entry


* reverse




find rule




continues with usual process




 GTWI K( I PKUU GE QTR VPGX G HQ VTCR VPG OVU WLFC GJV I PKYQJU OCT ICKF GE PGW SG 5
P GG D G XC J
FN W Q J U [ T V P G V C JV V C J Y F P C [ TV P G N C P K IK T Q G JV P G G Y V G D G E P G T G HHK F G J V U PK C V P Q E V C J V
[ T V P G G P Q V U W L J VK Y V P G O V U WL F C G J V G M C O W Q [   VPGO VU WLF # V PGOGEC NRG4 J V K 9
 [T VP G VP G O
GECNRGT GJV FPC [TVPG IPKUTGXGT C GTQO QYV GMC O WQ[ [TVPG NCPKIKTQ JECG TQ( UGKTVPG
H Q V Q N C PK UV N W U G T V K V W D UK J V Q F Q V [ C Y GN R OK U C UK   V P G O V U W L F # N C U T G X G 4  U GK T V P G
Y G P G M C O Q V F G G P W Q [ G M C V U K O C F P K H W Q [ P G J Y U GK T V P G G J V VK F G V• P C E W Q [ H +
entries
resulting
amount = $100 amount = 60 kwh
adjusting Usage Entry new Usage Event
event
replacement
event
adjusted
entries
amount = $500 resulting amount = 50 kwh
original Usage Entry original Usage Event
FGFTQEGT PGGD GXCJ FNWQJU VCJY FPC
FGFTQEGT UCY VCJY PGGYVGD VPGTGHHKF GJV VEGNHGT JEKJY UGKTVPG JVKY VPGXG UWQGPQTTG PC VUWLF#
&KHHGTGPEG #.LWUVOGPV

GE C N R GT F P C G UTG X GT G OC U G J V Q & U GU W   VPG O VU WLF # NC UTGXG4 V C J V J E C Q T R R C I P KUU G E
Q T R G O C U G J V H Q J E W O G U W Q V UK NN G Y T G J V C T M T Q Y P G G U G X• + J EK J Y G X K V C P T G V N C G J 6
 FT C Y M Y C V W D G N D KUU Q R P K U V P G X G Q Y V P G G Y V G D
E P G T G H HK F G J V V W Q G T W I KH Q V U G N W T I P K V U Q R G J V PK U V T C O U [ T C U U G E G P G J V I P K F NK W $  U G K T V P G
I P KV U WL F C G U G J V G V CN W E C E Q V Y Q J V W Q G T W I KH QV [ T V W Q [ U C U G O Q E P Q KV C E K N R O Q E G J 6
MTQY VK IPKMC/
 U V P G O G E CN R G T
F P C UN C U T G X G T G J V J I W Q T J V [ C Y T W Q [ M T Q Y Q V I P K X C J P C JV T G J V C T V P G O V U WL F C T C N W EK V T C R
C [ D FG UW CE GE PGT GHH KF G JV G G U [ NKU C G P C E WQ ; T GT CG NE U IP KJ V GM C O P CE QUN C VK G V C GTE
QV G X C J W Q [ U G KTV P G H Q V P W Q O C G JV P Q P Y Q F V W E U K J V U G Q F V U W L V Q 0  UV U G I I WU     GT W
I K ( U C [ T V P G I P K V U W L F C G P Q J VK Y U G K TV P G U W Q G P Q T T G N C T G X G U Z KH P G VH Q P C E W Q [ F G G F P+
[TV PG G PQ JVKY U VPGX G NCT GXGU IP KI PCJ %  GTWI K(
amount = 50kwh amount = 50kwh amount = 50kwh
:Usage Event :Usage Event :Usage Event
new events
amount = ($550)
:Adjustment
:Entry
old events
amount = 75kwh amount = 80kwh amount = 50kwh
:Usage Event :Usage Event :Usage Event
amount = $750 amount = $800 amount = $500
:Entry :Entry :Entry

V PW QE EC G IC UW GJ V HQ [ RQ E
C I PK M CV U P C G O U K J V [N N CK V P G U U '  U V P W Q E E C Y Q F C J U H Q V G U C G V C G T E QV U K R G V U V U T KH G J 6
U GKTVP G NCTG XGU JVKY VPW QEEC GICU W C JVKY GOQ VUWE C VP KQR I PKVTCV U GJ 6     G T W IK (
: Customer
Usage Account
amount = $750 amount = $800 amount = $500
:Entry :Entry :Entry
 U W Q G P Q T T G G T C Y Q P M Y Q P G Y V C J V U GK T V P G G J V I P K P K C V P Q E V P W Q E E C G I C U W C J VK Y T G O Q V
U W E C J V K Y P K I G D G 9  G EK V E C T R PK M T Q Y V J I K O V K Y Q J UK U K J V [ T C O O W U G V K T V C U• V C J 6
 UV P W Q E E C Y Q F C JU G J V F P C UV P W Q E E C N C GT G J V P G G Y V G D U G E P G
T GH HK F G J V V U Q R F P C G V C N W EN C E P G J 6  U V P W Q E E C Y Q F C J U H Q V G U G V C T C R G U C PK O G J V Q F V W D






:Entry :Entry :Entry

amount = $500 amount = $800 amount = $750




Usage Account



: Customer



shadow account




:Entry :Entry :Entry

amount = $500 amount = $800 amount = $750




 GTWI K( UVP WQEEC YQFCJ U R W IPK VVG 5


U V PW QEE C YQ FC JU G J V Q V U N CUT G XGT I P KU W F GUU GE QTR GT C U V PG X G Y G P G JV Y Q0





:Entry :Entry :Entry

amount = $500 amount = $800 amount = $750




Usage Account

balance = $2050


: Customer


shadow account

balance = $1500




:Entry :Entry :Entry

amount = $500 amount = $800 amount = $750


:Entry :Entry :Entry

amount = $500 amount = ($800) amount = ($750)


:Entry :Entry :Entry

amount = $500 amount = $500 amount = $500




    G T W IK ( U GKTVP G YGP FPC UNCUTG XT GT GJ V IPKV UQR TGVH #


[T V P G P C VU Q R F P C V P W Q E E C N C GT F P C Y Q F C JU G J V H Q G E P C N C D G J V G T C R O Q E G Y P G J 6
 G E P G T G H HK F G J V T Q H
 G X Q D C V W Q D C F G M N C V G X •+ T Q K X C J G D V P G O V U W L F C G JV V W Q [ T T C E P C E F Q J V G O U U G E Q T R
U• V +  V P G X G T G J V Q [ P C G M KN V U WL F GN W F G J E U G D Q V V K U Y Q NN C V P G X G P C U C V P G O V U WL F C G J V I PK
V C G T 6      G T W I K ( V P G OV U W L F C G J V U G U U G E QT R V C J V E K I Q N G J V J V K Y T G JV G I Q V V P G OV U W L F C
T C N W EK V T C R C H Q UN K C V G F G J V U F T Q E G T V C J V U U CN E V P G OV U W L F C P C G M C O Q V F Q Q I U •V K [ C Y
F G NN Q TV P Q E C PK E K I QN G J V I P K T Q V E C H U C N N G Y U C P Q U G Q I V C J Y H Q M E C T V R G G M QV T G F T Q P+
U VP WQ EE C
Y Q F C J U F P C N C P K I T Q G J V P G G Y V G D G E P G T GH HK F G J V U V P W Q E E C N C PK IK T Q G J V Q V V U Q R –
U V P W Q E E C Y Q F C J U G J V V U PK C I C UV P G X G Y G P G J V U U G E Q T R –
U V P W Q E E C Y Q F C J U G J V VU P K C I C U V P G X G F N Q G JV GUT G X GT –
UV P W Q E E C Y Q F C JU G JV G V C GT E –
UC URGVU GUQJV G\KTCOOWU PCE G9
 FGFTCEUKF
GD Y QP PCE UVP WQEEC YQFCJ U GJ 6 [TV PG IPK VUW LFC GJV IPK VUQ R FPC I PKVCGTE TG VH #  GTWI K(
: Customer
balance = $1500
Usage Account
amount = ($550) amount = $750 amount = $800 amount = $500
:Entry :Entry :Entry :Entry

V P G O V U WL F C P C G V C G T E QV G F Q E R W V G U E K U C D U P K C V P Q E V P G O V U WL F #
 F N W Q JU [ N D C D Q T R F P C  VPGO VU WLF #
 G E CN R G P Q P K I P K U U G E Q T R V P G O V U W L F C T Q H F G F G G P
NC UTGXG4 TQH G O C U GJ V QF F NW QE W Q;
G F Q E G J V H Q NN C V U Q O N C G V C TV P G E P Q E P C E W Q [ U U CN E V P G X G V P G O V U WL F C P C I PK M C O [ $
GFQ% GNROC5
G NDK UU QR OK FP C FGX N QXP K PG GY VG D GT GJ YG OQ U U K C VC F N CU TGX GT
G JV I P K V E W T V U P Q E G T J I W Q J VN C G N D K U U Q R Q U N C U K I P K T Q V E C H G T G U T G X G T G J 6  V N W E K H H K F Q Q V
VQP UK  VPGOVUWLF # GEPGTGHHK& QV  VPGOVUWLF # NCUTGXG4 OQTH IPKTQVECHGT [NGVCPWV
TQ(   VPGOVUWLF# GEPGTGHHK& GUW [TCOOWU C TGHGTR [GJV HK  VPGOVUWLF# NCUTGXG4
G U W I PK N N G E P C E VK E K N R Z G G G U Q V V P C Y [ G J V H + V W Q F GK T T C E G T C U V P G O V U WL F C G JV Y Q J V W Q D C
M P K J V Q V V P C Y U V T G R Z G PK C O Q F G JV Y Q J P Q U F P G R G F [ NN C W U W F P C G N R O K U V Q P UK  
V P G O V U W L F # G E P G T G H HK & F P C   V P G O V U WL F # N C U T G X G 4 P G G Y V G D G E K Q J E G J V Q U P G X '
 V P W QEE C J E C G T Q H V P W Q E E C Y Q F C JU F P C N C W V E C G J V H Q G E P C N C D G J V P G G Y V G D G E P GT G H
HK F G JV V U WL UK U GK TV P G I P KV U W L F C G J V H Q G W N C X G J 6  T Q V C P K OK T E UK F G P Q [ N P Q U K G T G J V [ C Y
V C JV U C V P W Q E E C P C G X C J W Q [ P G J Y V U GK U C G U K U K J 6  U T Q V C P K OK T E UK F G O C U G J V J VK Y U GK T V P G
V G I Q V F G G P W Q [ U G KT V P G J EV C O Q 6  Q YV G JV P G G Y V G D G E P GT G HH K F G J V G V W R O Q E P C E W Q [
Q U U G K T V P G N C P K I K T Q J E K J Y J E V C O V P G O G E CN R G T G J V O Q T H U G K T V P G J EK J Y V W Q G T W I K H Q V F G G P
W Q [ G U W C E G D U K UK J 6  F G U W I PK G D U K   V PW Q EE # P G J Y F GU W PT G V V C R U K J V G GU Q V F P G V +
VK GUW QV PGJ9
VPG XG V PG O VUW LFC PC I PKMC /     G T W IK (
0..1 0..1
Adjustment
»
»
Accounting Event
old events old events




public class Adjustment extends AccountingEvent ...
private List newEvents = new ArrayList();
private List oldEvents = new ArrayList();

public Adjustment(MfDate whenOccurred, MfDate whenNoticed, Subject subject) {
super(null, whenOccurred, whenNoticed, subject);
}
public void addNew(AccountingEvent arg) {
newEvents.add(arg);
}
public void addOld(AccountingEvent arg) {
if (arg.hasBeenAdjusted())
throw new IllegalArgumentException
("Cannot create " + this + ". " + arg + " is already adjusted");
oldEvents.add(arg);
arg.setReplacementEvent(this);
}
UK JV G M KN G F Q E J VK Y V P G O V U WL F C G J V R W V G U P G J V P C E W Q ;
class Tester...
// original events
usageEvent = new Usage(
Unit.KWH.amount(50),
new MfDate(1999, 10, 1),
new MfDate(1999, 10, 15),
acm);
eventList.add(usageEvent);
Usage usage2 = new Usage (// snip constructor args
eventList.add(usage2);
Usage usage3 = new Usage (// snip constructor args
eventList.add(usage3);
eventList.process();

// replacement events
MfDate adjDate = new MfDate(2000,1,12);
Usage new1 = new Usage (// snip constructor args
Usage new2 = new Usage (// snip constructor args
Usage new3 = new Usage (// snip constructor args
Adjustment adj = new Adjustment(adjDate, adjDate, acm);
adj.addOld(usageEvent);
adj.addOld(usage2);
adj.addOld(usage3);
adj.addNew(new1);
adj.addNew(new2);
adj.addNew(new3);
eventList.add(adj);
eventList.process();

VPGXG GJV UUGEQTR GY PGJY PK UMEKM TQKXCJGD VPGOVUWLFC GJ6



class Adjustment...
private java.util.Map savedAccounts;
public void process() {
Assert.isFalse ("Cannot process an event twice", isProcessed);
adjust();
markProcessed();
}
void adjust() {
snapshotAccounts();
reverseOldEvents();
processReplacements();
commit();
secondaryEvents = new ArrayList();
secondaryEvents.addAll(oldEvents);
}
public void snapshotAccounts() {
savedAccounts = getCustomer().getAccounts();
getCustomer().setAccounts(copyAccounts(savedAccounts));
}
void reverseOldEvents() {
Iterator it = oldEvents.iterator();
while (it.hasNext()) {
AccountingEvent each = (AccountingEvent) it.next();
each.reverse();
}
}
void processReplacements() {
AccountingEvent[] list = (AccountingEvent[])newEvents.toArray(new AccountingEvent[0]);
for (int i = 0; i < list.length; i++){
list[i].process();}
}
public void commit() {
AccountType[] types = AccountType.types();
for (int i = 0; i < types.length; i++) {
adjustAccount(types[i]);
}
restoreAccounts();
}
public void adjustAccount(AccountType type) {
Account correctedAccount = getCustomer().accountFor(type);
Account originalAccount = (Account) getSavedAccounts().get(type);
Money difference = correctedAccount.balance().subtract(originalAccount.balance());
Entry result = new Entry (difference, MfDate.today());
originalAccount.addEntry(result);
resultingEntries.add(result);
}
public void restoreAccounts() {
getCustomer().setAccounts(savedAccounts);
}
C G M C O F P C G E C N R GH C U C Q V U V P W Q E E C N C P K IK T Q G J V G X Q O Q V T G K U C G V K F P W QH + G U C E U K J V P +
G J V U V P W Q E E C U • T G O Q V U W E G J V U G M C O U K J V V E G H H G P+  T G O Q V U W E G J V P K UN C P K I K T Q G J V H Q [ R Q E
 I PK R R C Y U F P C I P K Y Q F C J U G JV N N C J V K Y F G U W H P Q E V G I V • P Q F [ G J V
U V P W Q EE C N CP KI KTQ G JV QV G E PGT GHG T C U F N Q J G P Q [P C HK [ CY V C J6 U N CP KIKT Q F GV UW LF C G JV
J V K Y U V P W Q E E C U• T G O Q V U W E G J V G E CN R G T + G P Q F NN C U• V C J V G E P 1  U V P W Q E C N C P K IK T Q F G X C U
G J V Q V O G JV U F F C F P C U G K T V P G I PK V U W L F C G JV U G P K O T G V G F P Q K V C T G R Q VK O O Q E G J V P G J 6
U V P W Q E E C U T G O Q V U W E G J V V U P K C I C U U G E QT R F P C G U T G X GT G JV Q F + P G J 6  U V P W Q E E C Y Q F C J U

VCJV UK JVIPGTVU U•V+ UGKTVPG IPKVNWUGT GJV HQ [VKZGNROQE GJV UG\KOKPKO QUNC VWD UGKTVPG
G JV H Q N K C TV V K F W C P C G X C GN V Q P U G Q F VK V C JV UK P T GV V C R UK JV H Q HH Q G F C T V [ G M G J 6
VK GUW QV PGJ9
 UV P G X G FN Q
G U Q J V J V K Y [ VK N K D C V K F W C G J V J V K Y F G P T G E P Q E Q Q V V • P G T C W Q [ P T G VV C R U K J V I P K U W G T C W Q [
H K [N N C W U W J I W Q J VN C U G U Q R T W R I PK I I Q N T QH V P G X G F N Q G J V Q V O G J V M PK N N NK V U V W D U P Q KV E G N
NQE [GM GJV OQTH OGJV GXQ OGT VJIK O WQ[ UTGJVQ P+ [NVGNR OQE UGKTVPG FNQ GJV GVGNGF
P C E W Q [ U G U C E G O QU P +  U I P K J V V P G T GH H K F P C G O [ C O U G K T V P G F N Q G JV H Q F K T I P K V V G )
 GW N CX VE GT
T Q E G J V G X C J NN K Y J E K J Y U GK T V P G Y G P GV C G T E Q V [ C Y T C N W I G T G J V PK V P G X G Y G P G J V U U G E
Q T R P G J V F P C O G J V H Q F K T V G I V P G X G FN Q G J V P Q U G K T V P G G J V N N C F P K H W Q [ V P G X G T G J V Q P C
U V U WL F C V C J V V P G X G P C V G I W Q [ P G J 9  [ I GV C T V U V P G O V U WL F C GN R OK U C G VK W S UK U K J 6
MTQY VK IPKMC/
 U G K T V P G G J V H Q G W N C X G J V I PK V K F G T Q U G P Q Y G P J V K Y O G J V I P K E C N R G T F P C U G K T V P G
FN Q G J V I P K X Q O G T T G J V K G [ D U G K T V P G V E G T T Q E P K V U W L F C U V P G OV U W L F C V P G O G E C N R G 4
resulting entries
amount = $600 amount = 60 kwh
entries replacing Usage Entry new Usage Event
replacement event
:customer
adjusted event
amount = $500 amount = 50 kwh
original Usage Entry resulting entries original Usage Event
entries {destroyed}
U G P Q Y G P J VK Y O G J V I PK E C N R G T F P C U GK T V P G F N Q G J V I P K X Q O G T [ D V P G X G P C V U W L F #
4GRNCEGOGPV #.LWUVOGPV

 VK Q F P W Q U H K F P C V P G X G F GV U W L F C
P C U C J I P K U U G E Q T R G T C G Y V P G X G G J V HK G G U Q V M E G J E G Y F Q J V G O U U G E Q T R G J V P K P G J 6
}
return (replacementEvent != null);
protected boolean hasBeenAdjusted() {
}
adjustedEvent.replacementEvent = this;
this.adjustedEvent = adjustedEvent;
this.whenNoticed = whenNoticed;
this.whenOccurred = whenOccurred;
this.type = type;
("Cannot create " + this + ". " + adjustedEvent + " is already adjusted");
throw new IllegalArgumentException
if (adjustedEvent.hasBeenAdjusted())
{
MfDate whenNoticed, AccountingEvent adjustedEvent)
public AccountingEvent (EventType type, MfDate whenOccurred,
private AccountingEvent adjustedEvent, replacementEvent;
class AccountingEvent
 U V P G X G V P G O G E C N R G T F P C F GV U W L F C G J V T Q H F N GKH F P C T Q V E W TV U P Q E Y G P C J VK Y V T C V U G 9
 U V P G X G F N Q H Q P QK V GN G F G J V GN F P C J Q V F G T G V N C
G D QV U F G G P V P G X G I P KV P W Q E E C P Q F Q J V G O U U G E Q T R G J V V C J V U K G E P G T GHH K F PK C O G J 6
   G N W 4 I P KV U Q 2 T Q H G F Q E GN R O C U G J V P Q F G U C D U K G F Q E G N R O C U U K J 6
GFQ% GNROC5
 U GT WV E W T V U
I P KV P W Q E E C [ G M G J V Q V F G M P K N T G I P Q N Q P V W D U V P G X G FN Q G JV QV F G M P KN F P W Q T C U G K TV P G
FNQ GJV RGGM PGXG VJIKO WQ; PQKVCOTQHPK IPKUUGEQTR HQ FTQEGT GVGNR OQE C GXCJ
W Q [ P G J V FN W Q J U W Q [ J E K J Y UV P G X G F N Q G J V R G G M W Q [ U C I P Q N U #  N K C T V V K F W C G T K V P G G J V
GU QN V Q P U G Q F V K U O GN DQT R [ VK NK DC VK FW C U C J   V P G OV U W L F # V P G O G E C N R G 4 V J I W Q J V N #
 G W S G J E T Q V P G O G V CV U NN K D C H Q V W Q I PK F P G U G JV T Q F QK T G R
N C K E P C P KH C H Q F P G G J V G D [ C O F Q K T G R I P K U Q N E G J 6  G X K V C P T G V N C G J V G U W W Q [ V C J V T G V H C
  V P G O V U WL F # V P G O G E C N R G 4 G U W W Q [ F G U QN E G T C U V P W Q E E C G J V G T G J Y V PK Q R G O Q U N K V P 7
  VPGOVUWLF# GEPGTGHHK& TQ  VPGOVUWLF # NCUTGXG4 TGJVKG JVKY  VPG OVUWLF#
V P G O G E C N R G 4 U G PK D O Q E P Q K V C W VK U P Q O O Q E V U Q O G J 6  VK G U W V • P C E W Q [ G OK V G J V H Q V U Q O
F GG F P K QU   V P G O V U W L F # V P G O G E CN R G 4 J V K Y O G N D Q T R [ G M G J V UK G I C V P C X F C U K F V C J 6
U GKT V PG FN Q GJ V H Q UKU CD G JV P Q N NK D C V W Q I PK F P G U U C JE WU
P Q K V E C G N D C U T G X G T TK G O Q U P G M C V G X• W Q [ H K O G N D Q T R I K D C UK J E K J Y U GK T V P G N C PK I K T Q G J V
H Q G E C T V Q P G X C J W Q [ U P C G O T G X G Y Q J [ V K EK N R O K U UK J 6  U G K TV P G I P KV U W L F C F P C U N C U T G X G T
H Q U U C O G J V O Q TH U GK T V P G V E G T T Q E G JV V W Q G T W IK H Q V G X C J W Q [   V P G OV U W L F # G E P G T G H
HK& FPC  VPGOVUWLF# NCUTGXG4 JVK9 UGKTVPG VEGTTQE GJV GTC GGU WQ[ UGKTVPG [NPQ GJV





public void process() {
Assert.isFalse ("Cannot process an event twice", isProcessed);
if (adjustedEvent != null) adjustedEvent.undo();
findRule().process(this);
isProcessed = true;
}
public void undo() {
Entry[] entries = getResultingEntries();
for (int i = 0; i < entries.length; i++)
getSubject().removeEntry(entries[i]);
undoSecondaryEvents();
resultingEntries = null;
}
private void undoSecondaryEvents(){
Iterator it = getSecondaryEvents().iterator();
while (it.hasNext()) {
AccountingEvent each = (AccountingEvent) it.next();
each.undo();
}
}


<<

. 2
( 2)