伝票の作成と保存

まずは、単純な伝票の作成と保存を実装します。在庫の増減などの機能は後から実装します。

この章では以下の内容について学習できます。

伝票を保存する

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"

より簡単にする

コードが長いので、もっと簡単に作成できるように変更しましょう。

上記の処理をInvoiceInvoiceItemに分担し、それぞれにメソッドを追加します。併せて、入金もできるようにします。

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コレクションの保存は、リレーションの条件にマッチしたレコードをすべて削除した上で行われます。これにより、コレクションに存在しない行が残るなどの矛盾を排除します。

トランザクションを使って保存する

では次に、トランザクション内で保存することでInvoiceInvoiceItemが矛盾しないようにします。

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からダウンロードできます。

  1. マスターデータの作成
  2. 在庫の増減