ゲーム化!tomo_manaのブログ

ゲーム化!tomo-manaのブログ

Unityでゲームを作る方法について紹介しています

Unity×会計#8 決算仕訳の不具合修正(損益計算)(Unity 2019.4.4f1)

f:id:tomo_mana:20211220010925p:plain

今回は、第6回の決算処理の損益計算が間違っていたので、修正します。
tomo-mana.hatenablog.com

f:id:tomo_mana:20210920141605p:plain
#6損益計算の修正

また、次回からの仕入単価の変動に対応するに先立って、第6回では一緒くたにしていた入力と処理を分離します。

入力と処理の分離

第6回で作成した入力は「仕入」「売上」「決算」の3つのボタンで成り立っていて、それぞれ仕入処理、売上処理、決算処理をしていました。仕入・売上処理は試算表を更新して、決算処理は棚卸して、損益計算書貸借対照表を作成しました。

f:id:tomo_mana:20210920144550p:plain
入力部と処理部の分離(#6)

ちょうど、仕入処理、売上処理、決算処理が入力と処理の分断ポイントになるため、ここでファイルを分けます。

ここでは修正コードは省略します。リファクタリングとしては割と取り組みやすい、移植元ファイルの関数が移植先ファイルの同名の関数をコールするように修正します。

(修正前)ファイルAで入力と処理を実施

// ファイルA
void ButtonClicked()
{
    ExecuteXx();
}
void ExecuteXx()
{
    // 処理
}

(修正後)ExecuteXx() 関数で入力と処理を分離し、ファイルAはファイルBクラスの同名の関数を呼びます。ファイルB内のクラスはファイルAの処理を切り取ってきたものです。

// ファイルA
ファイルB b;
void ButtonClicked()
{
    b.ExecuteXx();    // ファイルBの同名の関数をコールするだけ
}
// ファイルB
void ExecuteXx()
{
    // 処理(ファイルAで行っていた処理)
}

損益計算書の修正

第6回で作った決算処理は、仕入の決算仕訳(損益計算書への反映方法)が間違っていました。以下の図のように修正を入れます。

f:id:tomo_mana:20210920141605p:plain
#6損益計算の修正

修正後のコードは以下の通りです。

処理部(SalesTransaction.cs)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// 試算表
public class TrialBalance {
    public int cash;        // 現金
    public int purchase;    // 仕入
    public int sales;       // 売上
    public int q_remain;    // 在庫残
    public int q_total;     // 在庫総購入量
    
    public void Initialize(int c, int p, int s, int qr, int qt){
        cash = c;
        purchase = p;
        sales = s;
        q_remain = qr;
        q_total = qt;
    }
}
// 棚卸
public class InventoryDB {
    public int q_purchase;  // 仕入個数
    public int q_sales;     // 売上個数
    public int q_initial;   // 繰越個数
    
    public void Initialize(int p, int s, int i)
    {
        q_purchase = p;
        q_sales = s;
        q_initial = i;
    }
}
// 貸借対照表
public class BalanceSheetDB {
    public int inventory;   // 繰越商品
    public int flexLiability;   // 流動資産
}
// 損益計算書
public class ProfitAndLossDB {
    public int expenses;    // 費用
    public int revenue;     // 収益
    public int netIncome;   // 利益
}

public class SalesTransaction : MonoBehaviour
{
    TrialBalance tb = new TrialBalance();
    InventoryDB iv = new InventoryDB();
    BalanceSheetDB bs = new BalanceSheetDB();
    ProfitAndLossDB pl = new ProfitAndLossDB();
    
    // 資本
    int firstCash = 10000;
    // 販売物の仕入単価(現在は固定)
    int unitPrice = 1000;
    // 販売物の売価(現在は固定)
    int unitSales = 1500;
    
    // ログ用の文字
    string[] msg_e = new string[]{
        "SUCCEED!",
        "LACK OF CASH!",
        "LACK OF REMAINS!",
        "START!"
    };
    
    public TrialBalance GetTrialBalance()
    {
        return tb;
    }
    public InventoryDB GetInventory()
    {
        return iv;
    }
    public BalanceSheetDB GetBalanceSheet()
    {
        return bs;
    }
    public ProfitAndLossDB GetProfitAndLoss()
    {
        return pl;
    }
    public void InitializeJournal()
    {
        tb.Initialize(firstCash, 0, 0, 0, 0);
    }
    public int ExecutePurchase()
    {
        int e = 0;
        if( tb.cash >= unitPrice ){
            tb.cash -= unitPrice;
            tb.purchase += unitPrice;
            tb.q_remain++;
            tb.q_total++;
            iv.q_purchase++;
        } else {
            e = 1;
        }
        return e;
    }
    public int ExecuteSale()
    {
        int e = 0;
        if( tb.q_remain > 0 ){
            tb.cash += unitSales;
            tb.sales += unitSales;
            tb.q_remain--;
            iv.q_sales++;
        } else {
            e = 2;
        }
        return e;
    }
    public void ExecuteReset()
    {
        InitializeJournal();
    }
    public void ExecuteSettle()
    {
        // 棚卸
        iv.q_initial = iv.q_purchase - iv.q_sales;
        // 損益計算
        pl.expenses = iv.q_sales * unitPrice;
        pl.revenue = tb.sales;    //売上個数 * 売価;
        pl.netIncome = pl.revenue - pl.expenses;
        // 貸借(繰越商品)
        bs.inventory = iv.q_initial * unitPrice;
        // 年度をまたぐ(初期化)
        tb.Initialize( tb.cash, bs.inventory, 0, tb.q_remain, 0 );
        // 繰越商品を仕入に再計上
        iv.Initialize( iv.q_initial, 0, 0 );
    }
}

入力部(InputWindow2.cs)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class InputWindow2 : MonoBehaviour, ILangSwitchHandler
{
    enum BUTTON_NAME {
        BUTTON_PURCHASED = 0,
        BUTTON_SALED,
        BUTTON_SETTLE,
        BUTTON_RESET,
    };
    
    [SerializeField]
    BalanceSheet bs = default;
    [SerializeField]
    ProfitAndLoss pl = default;
    [SerializeField]
    GameObject journalButtons = default;
    [SerializeField]
    TextMeshProUGUI log = default;
    [SerializeField]
    SalesTransaction ts = default;
    
    string[] msg_j = new string[]{
        "成功!",
        "現金不足!",
        "在庫不足!",
        "START!"
    };
    string[] msg_e = new string[]{
        "SUCCEED!",
        "LACK OF CASH!",
        "LACK OF REMAINS!",
        "START!"
    };
    
    LANG selected;
    
    int error_hold;
    void UpdateLog(int error)
    {
        TrialBalance tb = default;
        
        if( !ReferenceEquals(ts, null) ){
            tb = ts.GetTrialBalance();
        }
        if( !ReferenceEquals(tb, null) ){
            if( error == -1 ){
                error = error_hold;
            } else {
                error_hold = error;
            }
            if( !ReferenceEquals(log, null) ){
                //現金:xxxx/仕入:xxxx/売上:xxxx/在庫:xx/xx(@100)
                if( selected == LANG.LANG_ENG ){
                    log.text = msg_e[error] + "\nCash:" + tb.cash + "\nPurchases:" + tb.purchase + "\nSales:" + tb.sales + "\nRemain:" + tb.q_remain + "/" + tb.q_total + "(@1000)";
                } else {
                    log.text = msg_j[error] + "\n現金:" + tb.cash + "\n仕入:" + tb.purchase + "\n売上:" + tb.sales + "\n在庫:" + tb.q_remain + "/" + tb.q_total + "(@1000)";
                }
            }
        }
    }
    
    void InitializeJournal()
    {
        if( !ReferenceEquals(ts, null) ){
            ts.InitializeJournal();
        }
        UpdateLog( 3 );
    }
    
    void ExecutePurchase()
    {
        int e = 0;
        if( !ReferenceEquals(ts, null) ){
            e = ts.ExecutePurchase();
        }
        UpdateLog( e );
    }
    void ExecuteSale()
    {
        int e = 0;
        if( !ReferenceEquals(ts, null) ){
            e = ts.ExecuteSale();
        }
        UpdateLog( e );
    }
    void ExecuteReset()
    {
        if( !ReferenceEquals(ts, null) ){
            ts.ExecuteReset();
        }
        if( !ReferenceEquals(bs, null) ){
            bs.ChangeAreaValues( 10000, 0, 0, 0, 0 );
            pl.ChangeAreaValues( 0, 0 );
        }
    }
    void ExecuteSettle()
    {
        TrialBalance tb = default;
        BalanceSheetDB bsdb = default;
        ProfitAndLossDB pldb = default;
        
        if( !ReferenceEquals(ts, null) ){
            ts.ExecuteSettle();
            tb = ts.GetTrialBalance();
            bsdb = ts.GetBalanceSheet();
            pldb = ts.GetProfitAndLoss();
        }
        if( !ReferenceEquals(tb, null) ){
            if( !ReferenceEquals(bs, null) ){
                if( !ReferenceEquals(bsdb, null) ){
//                  bs.ChangeAreaValues( tb.cash, tb.q_remain * unitPrice, 0, 0, 0 );  // #6 計算は間違ってはいないが…
                    bs.ChangeAreaValues( tb.cash, bsdb.inventory, 0, 0, 0 );
                }
            }
            if( !ReferenceEquals(pl, null) ){
                if( !ReferenceEquals(pldb, null) ){
//                  pl.ChangeAreaValues( tb.purchase, tb.sales );  // #6 費用が多く計算されてしまう
                    pl.ChangeAreaValues( pldb.expenses, pldb.revenue );
                }
            }
        }
    }
    void ButtonClicked(int buttonNo)
    {
        switch( buttonNo ){
        case (int)BUTTON_NAME.BUTTON_PURCHASED:
            ExecutePurchase();
            break;
        case (int)BUTTON_NAME.BUTTON_SALED:
            ExecuteSale();
            break;
        case (int)BUTTON_NAME.BUTTON_SETTLE:
            ExecuteSettle();
          break;
        case (int)BUTTON_NAME.BUTTON_RESET:
            ExecuteReset();
          break;
        }
    }
    public void LangSelected(LANG selectedLang)
    {
        // 省略
    }
    // Start is called before the first frame update
    void Start()
    {
        // 省略
    }
}

動作テスト

ゲームのURL(前回までと同じURLです)
tomo-mana.hatenablog.com

(以上)