前回の続きで、価格変動を考慮した場合の決算処理を実装します。
今は、情報を最小限にするため、商品は1種類だけ、原価計算は総平均法で計算しています。
また、原価計算として総平均法を採用している場合、仕入帳と売上帳があれば原価計算ができるため、商品有高帳は一旦無視します。
商品が1種類の場合、集計作業が単純なため試算表を省略できます。
(本来は(1)棚卸と(2)損益計算の前に、全帳簿を参照して試算を行います)
決算処理
棚卸
期首繰越商品を含む仕入個数と、当期の売上個数をまとめ、次期の繰越商品数を求めます。
上記をまとめると、以下の表になります。
2枚目の表では、期首繰越商品と当期の仕入個数を分けていますが、1枚目の仕入(仕入帳)には期首繰越商品が含まれています。そのため、仕入帳と売上帳の差が繰越商品数になります。
// 期首繰越個数と仕入個数を分けて管理している場合は、第三引数を指定する void 棚卸(仕入個数, 売上個数, [期首繰越個数 = 0]){ this.仕入個数 = 仕入個数; this.売上個数 = 売上個数; this.期首繰越個数 = 期首繰越個数; this.期末繰越個数 = this.期首繰越個数 + this.仕入個数 - this.売上個数; } 棚卸( 仕入帳.個数, 売上帳.個数 ); //期首繰越個数と当期仕入数を分けて考える場合は以下 //if( 仕入帳.レコード数 != 0 ){ // 棚卸( 仕入帳.個数 - 仕入帳.レコード[0].個数, 売上帳.個数, 仕入帳.レコード[0].個数 ); //}
現実には、帳簿上の数と実際の在庫が合わないことがあるため、欠損処理が入りますが、ここでは省略しています。
原価計算(総平均法)
次に、商品原価を計算します。今回は総平均法で計算します。
総平均法では、全ての仕入個数と、全ての仕入額とを足し合わせ、仕入総額を仕入総数で割って求めるのでした。この計算対象となる仕入は全ての仕入です。当期にどれだけ売り上げたかに関わらず、期首繰越商品を含むすべての仕入個数で計算します。
// 総平均法 int 商品原価; if( 仕入個数 != 0 ){ 商品原価 = 仕入総額 / 仕入個数; } else { 商品原価 = 0; // ゼロ割防止 }
商品原価は整数型にしましたが、商品原価を小数点まで保持したい場合は、原価を10倍(小数点1位)、100倍(2位)、1000倍(3位)しておく(ゲタを履かせる)方法もあります。また、思い切ってfloatやdoubleなどの浮動小数点を使う方法もあります(ただし、浮動小数点は、思いがけない誤差が出る可能性があります)。
(端数処理について)
簿記の試験ではともかく、実際の会計では、原価が割り切れないことはよくあると思います(端数処理が必要)。
今回は、端数を来期に持ち越すといろいろ面倒なので、当期中に処理することにします。後述しますが、商品原価は損益計算書の「費用」と貸借対照表の「流動資産」で按分されます。当期中に処理する場合、損益計算書の「費用」に端数が反映されます。
試算表の作成(省略)
試算表は、取扱商品が2つ以上ある場合に必要になります。今回は省略します。
損益計算
棚卸と原価計算が終わると、費用が算出できるので、以下のように算出します。
void 損益計算( 仕入総額, 売上総額 ) { 費用 = 仕入総額; 収益 = 売上総額; 利益損失 = 収益 - 費用; } // 損益計算( 売上帳.個数 * 商品原価, 売上帳.総額 ); // 端数を来期に回す場合 損益計算( 仕入帳.総額 - 棚卸.期末繰越個数 * 商品原価, 売上帳.総額 ); // 端数を当期で回収する場合
貸借対照表の作成
売れ残った商品は繰越商品(流動資産)に計上します。箱型バランスシート※と呼ばれる形式を使用しているため、現金は流動資産に含めない形で計算しています。(通常は現金は流動資産に含まれます)
void 貸借対照表計算( 現金, 流動資産, 固定資産, 流動負債, 固定負債 ) { this.現金 = 現金; this.流動資産 = 流動資産; this.固定資産 = 固定資産; this.流動負債 = 流動負債; this.固定負債 = 固定負債 ; this.純利益損失 = 現金 + 流動資産 + 固定資産 - 流動負債 - 固定負債; } 貸借対照表計算( 現金出納帳.残高, 棚卸.期末繰越個数 * 商品原価, 0, 0, 0 );
繰り越しと初期化
(繰り越し)
現金は昨年の残高を期首に振り替え、繰越商品は期首仕入に振り替えます。売上はまたゼロからスタートです。
void 繰越() { 現金出納帳.初期化( 現金出納帳.残高 ); 仕入帳.初期化( 棚卸.期末繰越個数, 仕入帳.総額 - 棚卸.期末繰越個数 * 商品原価 ); 売上帳.初期化(); }
(初期化)
尚、創業直後(初期化)は以下のようになります。補助簿をまっさらにして、初期の手持ち現金で初期化します。
void 初期化()
{
現金出納帳.初期化( 初期現金 );
仕入帳.初期化();
売上帳.初期化();
}
初期化処理と繰越処理は似ているので共通化できそうです。ただ、今は処理を分けておくことにします(共通化することで余計な検討が増えそうな予感がするため)
入力フォーム(ボタン)の追加
決算処理のテストのため、第6回で追加したものと同じボタンをこちらの入力フォームにも追加します。
決算と初期化の入力部をSettleInputという名前でグループ化し(いつものEmpty Object)、決算ボタンをButtonSettle、初期化ボタンをButtonResetとします。
後述する入力フォーム用のコード(SettleInput.cs)を、ButtonSettleとButtonResetに取り付けて、各ボタンに処理(ButtonName)をアサインします。
尚、SettleInput は 貸借対照表、損益計算書、ログ出力、データアクセスAPIへの参照を持ちます。少し複雑になってきたため、画面の各オブジェクトとクラスの関係をまとめます。
貸借対照表(BalanceSheet)、損益計算書(ProfitAndLoss)、ログ出力(JournalLog) は過去の記事で作ってきたものです。今回修正を入れるデータ処理部との関係は以下の構成になっています。
コード
(決算・リセット入力フォーム:SettleInput.cs)
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class SettleInput : MonoBehaviour { enum BUTTON_NAME { BUTTON_SETTLE = 0, BUTTON_RESET, }; [SerializeField] BUTTON_NAME buttonName = BUTTON_NAME.BUTTON_SETTLE; [SerializeField] BalanceSheet bs = default; [SerializeField] ProfitAndLoss pl = default; [SerializeField] JournalLog log = default; [SerializeField] SalesTransaction2 ts = default; void UpdateLog(int error) { log.UpdateLog( error ); } void ExecuteSettle() { int e = -1; if( !ReferenceEquals(ts, null) ){ e = ts.ExecuteSettle(); } if( e == 0 ){ e = 3; BalanceSheetDB bsdb = ts.GetBalanceSheet(); ProfitAndLossDB pldb = ts.GetProfitAndLoss(); bs.ChangeAreaValues( bsdb.cash, bsdb.flexAssets, bsdb.fixedAssets, bsdb.flexLiability, bsdb.fixedLiability ); pl.ChangeAreaValues( pldb.expenses, pldb.revenue ); UpdateLog( e ); } } void ExecuteReset() { int e = -1; if( !ReferenceEquals(ts, null) ){ e = ts.ExecuteReset(); } if( e == 0 ){ e = 3; BalanceSheetDB bsdb = ts.GetBalanceSheet(); ProfitAndLossDB pldb = ts.GetProfitAndLoss(); bs.ChangeAreaValues( bsdb.cash, bsdb.flexAssets, bsdb.fixedAssets, bsdb.flexLiability, bsdb.fixedLiability ); pl.ChangeAreaValues( pldb.expenses, pldb.revenue ); UpdateLog( e ); } } void ButtonClicked(int buttonNo) { switch( buttonNo ){ case (int)BUTTON_NAME.BUTTON_SETTLE: Debug.Log("Button Clicked = " + buttonNo + "Settle!"); // OK ExecuteSettle(); break; case (int)BUTTON_NAME.BUTTON_RESET: Debug.Log("Button Clicked = " + buttonNo + "Reset!"); // OK ExecuteReset(); break; default: break; } } // Start is called before the first frame update void Start() { int i = 0; Button button; // InputFieldゲームオブジェクトを取得 button = gameObject.GetComponent<Button>(); if( !ReferenceEquals(button, null) ){ int ii = (int)buttonName; button.onClick.AddListener(() => ButtonClicked(ii)); i++; } } }
(データ処理部:SalesTransaction2.cs)
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; // 台帳(増減あり) public class SinglePoint { public List<Tuple<int, int>> l; int rsum; public void Add( int a ) { rsum += a; l.Add( new Tuple<int, int>(a, rsum) ); } public void Clear() { l.Clear(); rsum = 0; } public SinglePoint() { l = new List<Tuple<int, int>>(); rsum = 0; } public int Get() { return rsum; } } // 台帳(増加のみ) public class DoublePoint { public List<Tuple<int, int>> l; public void Add( int a, int b ) { rsum1 += a; rsum2 += b; l.Add( new Tuple<int, int>(a, b) ); } public void Clear() { l.Clear(); rsum1 = 0; rsum2 = 0; } int rsum1; // 個数 int rsum2; // 金額 public DoublePoint() { rsum1 = rsum2 = 0; l = new List<Tuple<int, int>>(); } public int Get(int id) { if( id == 0 ){ return rsum1; // 個数 } else if( id == 1 ){ return rsum2; // 金額 } else { return 0; } } } public class SalesTransaction2 : MonoBehaviour { // 試算表 TrialBalance tb = new TrialBalance(); // 棚卸 InventoryDB iv = new InventoryDB(); // 貸借対照表 BalanceSheetDB bs = new BalanceSheetDB(); // 損益計算書 ProfitAndLossDB pl = new ProfitAndLossDB(); // 資本 int firstCash = 100000; // 販売物の仕入単価(現在は固定) int unitPrice = 1000; // 現金出納帳 SinglePoint cash = new SinglePoint(); // 仕入帳 DoublePoint purchase = new DoublePoint(); // 売上帳 DoublePoint sales = new DoublePoint(); public BalanceSheetDB GetBalanceSheet() { return bs; } public ProfitAndLossDB GetProfitAndLoss() { return pl; } public void InitializeJournal() { // 現金出納帳 cash.Clear(); cash.Add(firstCash); // 仕入帳 purchase.Clear(); // 売上帳 sales.Clear(); } /* 略 */ // 繰り越し void CarryOver() { int[] tmp = new int[2]; // 現金の繰り越し tmp[0] = cash.Get(); cash.Clear(); cash.Add(tmp[0]); // 仕入の繰り越し tmp[0] = iv.q_carryover; tmp[1] = tmp[0] * unitPrice; purchase.Clear(); if( tmp[0] != 0 ){ purchase.Add(tmp[0], tmp[1]); } // 売上の繰り越し sales.Clear(); } // 決算処理 public int ExecuteSettle() { // 棚卸表の作成(今のところ仕入れてないものを売るは想定しない) iv.Set( purchase.Get(0), sales.Get(0) ); // 原価計算(端数は当期中に処理すること) if( purchase.Get(0) != 0 ){ unitPrice = purchase.Get(1) / purchase.Get(0); } else { unitPrice = 0; } // 試算表の作成(省略) // 損益計算 // pl.Set( iv.q_sales * unitPrice, sales.Get(1) ) // 端数を加味しない pl.Set( purchase.Get(1) - iv.q_carryover * unitPrice, sales.Get(1) ); // 貸借対照表 bs.Set( cash.Get(), iv.q_carryover * unitPrice, 0, 0, 0 ); // 繰り越し CarryOver(); return 0; } // 初期化処理 public int ExecuteReset() { InitializeJournal(); return 0; } /* 略 */ }
(データ構造:Transaction.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 int unit; // 仕入単価 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_initial; // 期首繰越商品数 public int q_purchase; // 仕入数 public int q_sales; // 売上数 public int q_carryover; // 期末繰越商品数 public void Initialize(int p, int s, int i) { q_purchase = p; q_sales = s; q_initial = i; q_carryover = 0; } void CalcCarryOver() { q_carryover = q_initial + q_purchase - q_sales; } public void Set(int q_purchase, int q_sales, int q_initial) { this.q_purchase = q_purchase; this.q_sales = q_sales; this.q_initial = q_initial; CalcCarryOver(); } public void Set(int q_purchase, int q_sales) { Set(q_purchase, q_sales, 0); } } // 貸借対照表 public class BalanceSheetDB { public int cash; // 現金 public int flexAssets; // 流動資産 public int fixedAssets; // 固定資産 public int flexLiability; // 流動負債 public int fixedLiability; // 固定負債 public int netAssets; // 純資産 void CalcNetAssets() { netAssets = cash + flexAssets + fixedAssets - flexLiability - fixedLiability; } public void Set( int cash, int flexAssets, int fixedAssets, int flexLiability, int fixedLiability ) { this.cash = cash; this.flexAssets = flexAssets; this.fixedAssets = fixedAssets; this.flexLiability = flexLiability; this.fixedLiability = fixedLiability; CalcNetAssets(); } } // 損益計算書 public class ProfitAndLossDB { public int expenses; // 費用 public int revenue; // 収益 public int netIncome; // 利益 void CalcNetIncome() { netIncome = revenue - expenses; } public void Set( int purchase, int sales ) { expenses = purchase; revenue = sales; CalcNetIncome(); } }
動作テスト
ゲームのURL(前回までと同じURLです)
tomo-mana.hatenablog.com
次回からは以下に取り組みます。
●5年分のデータを保持する(決算資料の配列化)
●複数の商品を扱えるようにする(仕入帳、売上帳の配列化)