InvoicePlane 請求書昨年(2022)の12月上旬に新しいPCを組み立て、二つの2TBのM.2の一つにDockerでの開発環境を整えていろいろやっていた。先月末(4月)にその開発環境がドライブごとお亡くなりになりました。トランセンド製でした。わずか5か月経ってもないのに・・・。すぐに保証の手続きを行い、なんと! 僅か5日ほどで新品が届きました。ありがとうございますトランセンド様。たぶんゴールデンウィーク前で手続きを早めてくれたのかなぁ・・・。正直ゴールデンウィーク後だと思っていました。

さて、そのドライブに入っていた変更済みのInvoicePlane・・・以前はSiwappを使っていたのですが、PHP版はあるにはあるが、う~~~~んって感じだったのでInvoicePlaneに変更しました。今回全部ゼロから環境を作成したので以前に気づかなかった点など含めて書いてみます。

InvoicePlane v1.6.1 beta2 and v1.6.1(確認済み)

変更後のPDFイメージ
InvoicePlane 請求書 InvoicePlane 見積書
 
PDFテンプレートの新規作成
デフォルトのファイルを直接編集すると、アップデート時に上書きしてしまう恐れがあるので、まず最初にやるべきことは、オリジナルテンプレートファイルの作成です。
application/views/invoice_templates/pdf
application/views/quote_templates/pdf
それぞれのフォルダ内にcustom_invoice_tmpl.phpなどと適当にphpファイルを作成し、デフォルトのファイルから内容をコピーして作成。
システム設定でそれぞれ請求書・見積書のテンプレートを作成したオリジナルのテンプレートファイルを選択して保存。
 
cssファイルの新規作成
これも同じくアップデートで上書きされるので新規作成し、内容をコピーする。
assets/core/css/custom-pdf.css
assets/invoiceplane/css/templates.css
assets/invoiceplane_blue/css/templates.css
作成するテンプレートで読み込むcssを変更できるので、とりあえずワンファイルだけcustom-pdf.cssのあるフォルダにoriginal-pdf.cssなどと適当に作成し、選択しているテーマにあわせ、invoiceplaneかinvoiceplane_blueのどちらかのcssファイルとcustom-pdf.css(空)の内容をコピペしておく。
 
作成したものをダウンロードできるようにしました。こちらからどうぞ!
 
金額表示
小数点以下を表示したくない場合
  1. application/config/number_formats.phpを開き、'number_format_us_uk'の値'decimal_point'の値を'.'から''(何も無い状態)に変更する。
  2. システムの設定 > 基本設定 > 金額の設定 > Number Formatで US/UK formatを選択して保存しなおす。
  3. この変更により、小数点以下は表示されない。
※アップデートすると上書きされるのでアップデート後同じ作業を行う。
※後述の問題で、これはやらない方が精神的によさそうです。なぜなら、小数点以下が四捨五入されてしまうようです。出力すると正解しか表示されませんが、請求内容を見ると正解なの?と疑いたくなるからです。
今回、最初は上記金額表示の設定を行っていたもののそれをやめたのでPDFでも小数点以下が表示されてしまいます。その対応として以下

<?php global $CI; $CI--->mdl_settings->settings['decimal_point'] = '';?>
 
上記の一行をテンプレートファイルの最上部に加えておくだけでPDFファイルの小数点以下表示が解消できます。
 
PDFフォント
私の場合、VLゴシックが昔から好きでSiwapp使ってた頃もVLでした。なのでここでも使います。
こちらを参考にさせていただきました。
 
まず、フォントをダウンロードし解凍したらttfファイルを vendor/mpdf/mpdf/ttfontsへ入れます。
 
application/helpers/mpdf_helper.phpの以下の部分に追記

    $mpdf = new \Mpdf\Mpdf([
        'tempDir' => UPLOADS_TEMP_MPDF_FOLDER
    ]);
その中に以下のように追記。

 $mpdf = new \Mpdf\Mpdf([
        'tempDir' => UPLOADS_TEMP_MPDF_FOLDER
        ,'fontdata' => [
            'vlg' => [
                'R' => 'VL-Gothic-Regular.ttf'
            ]
        ],
        'default_font' => 'vlg'
 ]);
その下あたりの以下の行をコメントアウト又は、true をfalseで。そのままだと、入れたフォントを使ってくれない。

//    $mpdf->useAdobeCJK = true;
 
ロゴ画像(httpsアクセスの場合)
httpsでアクセスするInvoicePlaneだと当然といえば当然なのだが、画像表示のHTMLもhttpsから始まるフルのURLで生成される。(PDF生成の場合だけ、それを考慮してくれれば良いのだが...) mPDF内部では、どうやら相対パスで画像にアクセスするらしく”画像が無い”というエラーでX印だけが表示されてしまう。以前は、テンプレート側で直接画像出力させて対応していたが、今回は上記のmpdf_helper.phpの書き換えで対応した。
$html = str_replace('https://yoursite.com/uploads/', '/uploads/', $html);
これで正しく表示される。これを書き入れる位置は、上記で追記した次の行あたりでOKです。というか、このpdf_createの関数内の$mpdf->WriteHTMLまでならどこでもOK。
 
日付表示の変更
日付表示を年月日表示に変更するには、
application/helpers/date_helper.phpを変更しなければいけません。
function date_formats()の部分

return [
    'd/m/Y' => [
        'setting' => 'd/m/Y',
        'datepicker' => 'dd/mm/yyyy',
    ],

return [
    'Y年m月d日' => [
        'setting' => 'Y年m月d日',
        'datepicker' => 'yyyy年mm月dd日',
    ],
    'd/m/Y' => [
        'setting' => 'd/m/Y',
        'datepicker' => 'dd/mm/yyyy',
    ],
年月日設定を足します。
変更後、システム設定から追加した設定を選択して保存します。
 
税額計算の切り捨て
以前は、こちらを参考にさせていただいて変更していましたが、上記にもあるように後述の問題でやめました。
 
重大な問題がありました。
消費税の計算ですが、上記では個々の品目に対して消費税を端数処理しその合計を消費税の合計としていますが、適格請求書等保存方式(インボイス制度)では「一枚の請求書や納品書ごとに、税率別に1回のみ」だそうです。InvoicePlaneでは現在、個々の税額を最後に合計して算出しています。これを税率別にまとめた小計を元に税額をださなければ正しい税額が出てきません。たとえば、軽減税率8%の商品が2つ10%の商品が3つあった場合、8%の商品の小計から8%で計算して端数処理、10%の商品の小計から10%で計算して端数処理してから合計をださなければなりません。適格請求書等保存方式(インボイス制度)でないならば問題ないようです。
さて、これ解決できるか!?
 
application/modules/invoices/models/Mdl_invoice_amounts.php

public function calculate($invoice_id)
    {
        // Get the basic totals
        $query = $this->db->query("
        SELECT  SUM(item_subtotal) AS invoice_item_subtotal,
		        SUM(item_tax_total) AS invoice_item_tax_total,
		        SUM(item_subtotal) + SUM(item_tax_total) AS invoice_total,
		        SUM(item_discount) AS invoice_item_discount
		FROM ip_invoice_item_amounts
		WHERE item_id IN (
		    SELECT item_id FROM ip_invoice_items WHERE invoice_id = " . $this->db->escape($invoice_id) . "
		    )
        ");
        $invoice_amounts = $query->row();
        $invoice_item_subtotal = $invoice_amounts->invoice_item_subtotal - $invoice_amounts->invoice_item_discount;
        $invoice_subtotal = $invoice_item_subtotal + $invoice_amounts->invoice_item_tax_total;
        $invoice_total = $this->calculate_discount($invoice_id, $invoice_subtotal);
        // Get the amount already paid
これを以下に

public function calculate($invoice_id)
    {
        // Get the basic totals
        $tax_rates = array();
//        税設定なしの場合
        $tax_rates[0] = 0;

        $query = $this->db->query("
            SELECT tax_rate_percent AS percent, tax_rate_id AS id, tax_rate_name
            FROM ip_tax_rates
        ");

        $rows = $query->result_object();
        if(count($rows) > 0)
        {
            foreach($rows as $row)
            {
                $tax_rates[$row->id] = $row->percent/100;
            }
        }

        $query = $this->db->query("
            SELECT  SUM(a.item_subtotal) AS invoice_item_subtotal,
                    SUM(a.item_tax_total) AS invoice_item_tax_total,
                    SUM(a.item_subtotal) + SUM(a.item_tax_total) AS invoice_total,
                    SUM(a.item_discount) AS invoice_item_discount,
                    b.item_tax_rate_id AS id
            FROM ip_invoice_item_amounts AS a
            INNER JOIN ip_invoice_items AS b ON a.item_id = b.item_id
            WHERE b.invoice_id = " . $this->db->escape($invoice_id) . "
            GROUP BY b.item_tax_rate_id
        ");
        $rows = $query->result_object();

        $invoice_item_subtotal = 0;
        $invoice_subtotal = 0;
        $invoice_amounts = new stdClass();
        $invoice_amounts->invoice_item_tax_total = 0;
        $invoice_total = 0;

        if(count($rows) > 0)
        {
            foreach($rows as $row)
            {
//                実際は、項目別のディスカウントは、税の対象外になっているので使えない
                $temp = $row->invoice_item_subtotal - $row->invoice_item_discount;
                $invoice_item_subtotal += $temp;
//               税別のTaxを計算
                $row->invoice_item_tax_total = floor($temp*$tax_rates[$row->id]);
//                請求書合計
                $invoice_subtotal += $temp + $row->invoice_item_tax_total;
//                税額合計
                $invoice_amounts->invoice_item_tax_total += $row->invoice_item_tax_total;
            }

            $invoice_total = $this->calculate_discount($invoice_id, $invoice_subtotal);
        }

        // Get the amount already paid
これは、請求書側というか・・・同じことするのに見積書側と別なんて・・・
application/modules/quotes/models/Mdl_quote_amounts.php

  // Get the basic totals
  $query = $this->db->query("
      SELECT SUM(item_subtotal) AS quote_item_subtotal,
    SUM(item_tax_total) AS quote_item_tax_total,
    SUM(item_subtotal) + SUM(item_tax_total) AS quote_total,
    SUM(item_discount) AS quote_item_discount
FROM ip_quote_item_amounts
WHERE item_id
    IN (SELECT item_id FROM ip_quote_items WHERE quote_id = " . $this->db->escape($quote_id) . ")
      ");

  $quote_amounts = $query->row();

  $quote_item_subtotal = $quote_amounts->quote_item_subtotal - $quote_amounts->quote_item_discount;
  $quote_subtotal = $quote_item_subtotal + $quote_amounts->quote_item_tax_total;
  $quote_total = $this->calculate_discount($quote_id, $quote_subtotal);

  // Create the database array and insert or update

// Get the basic totals
        $tax_rates = array();
//        税設定なしの場合
        $tax_rates[0] = 0;

        $query = $this->db->query("
            SELECT tax_rate_percent AS percent, tax_rate_id AS id, tax_rate_name
            FROM ip_tax_rates
        ");

        $rows = $query->result_object();
        if(count($rows) > 0)
        {
            foreach($rows as $row)
            {
                $tax_rates[$row->id] = $row->percent/100;
            }
        }

        $query = $this->db->query("
            SELECT SUM(a.item_subtotal) AS quote_item_subtotal,
              SUM(a.item_tax_total) AS quote_item_tax_total,
              SUM(a.item_subtotal) + SUM(item_tax_total) AS quote_total,
              SUM(a.item_discount) AS quote_item_discount,
              b.item_tax_rate_id AS id
          FROM ip_quote_item_amounts AS a
          INNER JOIN ip_quote_items AS b ON a.item_id = b.item_id
          WHERE b.quote_id = " . $this->db->escape($quote_id) . "
          GROUP BY b.item_tax_rate_id
            ");

        $rows = $query->result_object();

        $quote_item_subtotal = 0;
        $quote_subtotal = 0;
        $quote_amounts = new stdClass();
        $quote_amounts->quote_item_tax_total = 0;

        if(count($rows) > 0) {
            foreach ($rows as $row) {
//                実際は、項目別のディスカウントは、税の対象外になっているので使えない
                $temp = $row->quote_item_subtotal - $row->quote_item_discount;
                $quote_item_subtotal += $temp;
//               税別のTaxを計算
                $row->quote_item_tax_total = floor($temp * $tax_rates[$row->id]);
//                請求書合計
                $quote_subtotal += $temp + $row->quote_item_tax_total;
//                税額合計
                $quote_amounts->quote_item_tax_total += $row->quote_item_tax_total;
            }
        }

        $quote_total = $this->calculate_discount($quote_id, $quote_subtotal);

        // Create the database array and insert or updatに
に変更。
何やってるかというと税率別にグループにしてトータルを出し、そのトータルに税率をかけて小数点以下を切り捨てています。(できてるつもり・・・)
 
さて、ここで本体を変更したファイル(アップデートで上書きされてしまうファイル)を一覧にしておきます。
  • application/config/number_formats.php
  • application/helpers/mpdf_helper.php
  • application/helpers/date_helper.php
  • application/modules/invoices/models/Mdl_invoice_amounts.php
  • application/modules/quotes/models/Mdl_quote_amounts.php
これら、5つのファイルはアップデート時にどのように更新されるかわからないので変更点を把握できるように別の場所にコピーを保存しておくとよいかもしれません。
※v1.6.1に更新し、beta2で変更したものがそのまま使えることを確認しました。
 
※不具合・間違い・勘違い等あればお知らせください。