沖銷調整

2006 年 1 月 2 日

這是 進一步的企業應用程式架構開發 寫作的一部分,我在 2000 年代中期進行。遺憾的是,自那時起,太多其他事情吸引了我的注意力,所以我沒有時間進一步研究它們,我也看不到在可預見的未來會有太多時間。因此,此材料在很大程度上仍處於草稿形式,在我能夠找到時間再次研究它之前,我不會進行任何更正或更新。

運作方式

對於需要調整的每個項目,您建立兩個新項目。一個項目是前一個項目的簡單沖銷,使用相同的發生日期和相同金額但符號相反。然後,您發布一個新項目,這就是舊金額應該是什麼。

因此,假設我們記錄了 3 月份使用 50kwh 小時的電力。此事件最初記錄在 4 月 5 日,並在 4 月 10 日處理。這產生了 圖 1 中的物件

圖 1:調整前的事件。

然後在 6 月 1 日,我們意識到發生錯誤,金額應該是 80 kwh。這導致了 圖 2 的結構。

您可能會驚訝地發現,沖銷項目出現在舊事件上,而不是新事件上。這是因為新事件本身可能會在稍後進行調整,在這種情況下,我們不希望產生沖銷沖銷項目的項目。因此,將它們放在原始事件上更有意義。我們還將該事件標記為已調整,以清楚地表明它已調整,因此不應再次調整。

圖 2:調整後的事件

使用 沖銷調整 會在這些沖銷對中產生大量項目。因此,這表示如果有人想要查看項目清單,他們通常會希望查看排除所有沖銷對的項目。因此,您需要提供一個查詢方法來過濾此資訊。由於可能會涉及大量資料,這通常也會影響您的資料庫查詢。

何時使用

反轉調整 是當分錄無法變更,因此無法使用 替換調整 時的簡單替代方案。其主要缺點是會產生大量分錄:一旦完成,您會為每個先前分錄新增三個分錄。執行此動作數次後,您會產生大量分錄,其中許多為反向配對。雖然您可以篩選這些分錄,但它們仍然會造成困擾。

差異調整 是主要的替代方案。通常,在它們之間的選擇取決於您的使用者想要如何查看資訊。如果分錄可以變更,最好使用 替換調整。您通常會發現分錄在特定日期之前可以變更,但在該日期之後無法變更。這會讓您使用 反轉調整替換調整 的組合。

範例:反轉不正確的用電量(Java)

以下是我們不正確用電量的範例,以及如何反轉。

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

在此實作中,我們提供會計事件的建構函式,其中包含調整事件的時段。

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;

在處理過程中,如果存在調整事件,則會先反轉該事件。反轉僅涉及為每個原始分錄過帳相反分錄。然後,可以像平常一樣完成事件處理的其餘部分。

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