伝票の作成と保存
まずは、単純な伝票の作成と保存を実装します。在庫の増減などの機能は後から実装します。
この章では以下の内容について学習できます。
- リレーションオブジェクトの同時保存
- トランザクションの開始と終了
- 例外処理
伝票を保存する
id = 1
の顧客にAPPLE1
という商品を1個売上するサンプルです。 APPLE1
の価格はProduct
マスターを検索して求めます。
const TAX_RATE = 0.08;
const SALES = 0;
$cust = Customer::find(1);
$inv = new Invoice;
// 日付
$inv->date = date("Y/m/d"); // 今日を設定
// 得意先
$inv->customer()->associate($cust); // $cust->idを$inv->customer_idにコピー
// 商品の検索
$prod = Product::index(1)->findOrFail('APPLE1');
// 行の作成
$item = new InvoiceItem;
$qty = 1;
$item->product_code = $prod->code;
$item->product_description = $prod->description;
$item->line_type = SALES;
$item->price = $prod->price;
$item->quantity = $qty;
$item->amount = $prod->price * $qty;
$item->tax = (int)(($item->amount * TAX_RATE) + 0.5);
// 追加
$inv->items->add($item);
// itemsコレクションも一緒に保存する
$inv->save(Model::SAVE_WITH_RELATIONS);
PHPのdate
関数を使う際は、事前にphp.ini
にタイムゾーンを設定しましょう。
[Date]
date.timezone = "Asia/Tokyo"
より簡単にする
コードが長いので、もっと簡単に作成できるように変更しましょう。
上記の処理をInvoice
とInvoiceItem
に分担し、それぞれにメソッドを追加します。併せて、入金もできるようにします。
class Invoice extends Model
{
// (省略)
public $date;
public function __construct()
{
parent::__construct();
$this->amount = new InvoiceAmount;
// 日付のデフォルトは今日になるようにする
$this->date = date("Y/m/d");
}
// 売上行を追加する
public function addSalesLine($code, $qty)
{
$prod = Product::index(1)->findOrFail($code);
$item = new InvoiceItem;
$item->assignSales($prod, $qty);
$this->items->add($item);
return $item;
}
// 入金行を追加する
public function addPaymentLine($amount, $desc)
{
$item = new InvoiceItem;
$item->assignPayment($amount, $desc);
$this->items->add($item);
return $item;
}
}
class InvoiceItem extends Model
{
const SALES = 0;
const PAYMENT = 1;
const TAX_RATE = 0.08;
// (省略)
// 商品と数量を指定してこの売上の値を設定する
public function assignSales($prod, $qty)
{
$this->product_code = $prod->code;
$this->product_description = $prod->description;
$this->line_type = self::SALES;
$this->price = $prod->price;
$this->quantity = $qty;
$this->amount = $prod->price * $qty;
$this->tax = (int)(($this->amount * self::TAX_RATE) + 0.5);
}
// 金額と詳細を指定して入金を設定する
public function assignPayment($amount, $desc)
{
$this->product_code = 'PAYMENT';
$this->product_description = $desc;
$this->line_type = self::PAYMENT;
$this->amount = $amount;
}
}
これで、先ほどと同じ伝票が以下のように作成できます。
$cust = Customer::find(1);
$inv = new Invoice;
$inv->customer()->associate($cust); // $cust->idを$inv->customer_idにコピー
$inv->addSalesLine('APPLE1', 1);
$inv->save(Model::SAVE_WITH_RELATIONS);
Model::SAVE_WITH_RELATIONS
は、$inv->items
コレクション内のアイテムも同時に保存するように指定しています。
Note: $inv->items
コレクションの保存は、リレーションの条件にマッチしたレコードをすべて削除した上で行われます。これにより、コレクションに存在しない行が残るなどの矛盾を排除します。
トランザクションを使って保存する
では次に、トランザクション内で保存することでInvoice
とInvoiceItem
が矛盾しないようにします。
save
メソッドをトランザクションでラップしたsaveWithTransaction
メソッドを作成します。同様に伝票削除のメソッドdeleteWithTransaction
も作ります。
save
は例外をスローするので、必ず例外を補足してrollBack
しましょう。
class Invoice extends Model
{
// (省略)
// 必要なテーブルを事前準備する
private function prepareTables()
{
Stock::prepareTable();
DailySummary::prepareTable();
InvoiceItem::prepareTable();
}
public function saveWithTransaction()
{
try {
$this->prepareTables();
DB::beginTrn(Transactd::MULTILOCK_GAP);
$this->save();
DB::commit();
} catch (Exception $e) {
DB::rollBack();
throw $e;
}
}
public function deleteWithTransaction()
{
try {
$this->prepareTables();
DB::beginTrn(Transactd::MULTILOCK_GAP);
$this->delete();
DB::commit();
} catch (Exception $e) {
DB::rollBack();
throw $e;
}
}
トランザクションを開始する際には、使用するテーブルが事前にオープンされていなければなりません。 prepareTable
メソッドで事前に準備しておきます。このメソッドはすでにテーブルがオープンされていれば無視されます。重複してオープンされてしまうことはありません。
これからはsave
の代わりにsaveWithTransaction
メソッドを使って保存します。
ソースコード
ここまでのソースコードはGitHub Gistからダウンロードできます。