EC-CUBE4で簡易的に領収書や請求書をマイページに表示させる方法

EC-CUBE Advent Calendar 2021 12日目の記事です。

EC-CUBE4では管理画面から納品書を出力する機能はありますが、領収書や請求書を出力する機能がありません。 納品書の機能で件名を変更すれば対応をしようと思えば可能ですが、購入した方のマイページにも領収書や請求書を出力できるようにしたいという要望があります。

今回はその方法を説明します。

領収書・請求書のフォーマット作成

管理画面では納品書はPDFで出力されますが、今回は領収書と請求書はHTMLとしてブラウザに出力後、ブラウザの機能を利用して印刷させる方法とします。Amazonでお馴染みの方法となります。

領収書と納品書のフォーマットですが、twigとして作成する必要があります。以下にサンプルとして載せておきますので自由に改変してください。 それぞれのファイルの配置場所もapp/template/default/Mypage直下となります。

  • app/template/default/Mypage/print_receipt.twig (領収書フォーマット)
<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <title>領収書 | {{ BaseInfo.shop_name }}</title>
    <style>
        .container {
            width: 900px !important;
            font-size: 12px;
            margin-top: 20px;
        }

        .logo {
            float: left;
            font-size: 20px;
            font-weight: bold;
        }

        .inshi {
            border: 2px solid #ddd;
            padding: 10px;
            text-align: center;
            margin-right: 5%;
        }

    </style>
    <style media="print">
        #print-footer {
            display: none;
        }

        input[type="text"] {
            outline: 0;
            border: none;
        }

        button,
        #edit,
        #mod {
            display: none;
        }
    </style>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script src="{{ asset('assets/js/vendor/printThis.js') }}"></script>
    <script>
        $(function() {

            $('#print').click(function() {
                $('body').printThis({
                    importStyle: true
                });
            });

            $('#edit').click(function() {
                var name = $('.print_name').text();
                var input = $('<input type="text" id="input-name">').val(name);
                $('.print_name').empty().append(input);
                $('#edit').hide();
                $('#mod').show();
            });

            $('#mod').click(function() {
                var newname = $('#input-name').val();
                $('.print_name').empty().text(newname);
                $('#input-name').remove();
                $('#edit').show();
                $('#edit').removeAttr('style');
                $('#mod').hide();
                showEx();
            });

            function showEx() {
                var tmp = $('.print_name').text();

                if (tmp == '') {
                    $('.print_name_ex').hide();
                } else {
                    $('.print_name_ex').show();
                }
            }

        })
    </script>
</head>
<body>
<div class="container">
    <div class="logo">
        {{ BaseInfo.shop_name }}
    </div>
    <div class="text-end">No. {{ Order.order_no }}</div>
    <div class="text-end">注文日 : {{ Order.order_date|date_format(null, 'Y年m月d日') }}</div>
    <h1 class="text-center">領収書</h1>
    <div class="row">
        <div class="col-7">
            <h2 id="name"><span class="print_name">{% if Order.company_name %}{{ Order.company_name }} 御中{% else %}{{ Order.name01 }} {{ Order.name02 }} </span><span class="print_name_ex"></span>{% endif %}</h2>
            <button type="button" id="edit">名称変更</button>
            <button type="button" id="mod" style="display: none;">適用</button>
        </div>
    </div>
    <hr>

    <table class="table text-center">
        <tbody>
        <tr>
            <td class="h3">{{ Order.payment_total|price }} (税込)</td>
        </tr>
        <tr>
            <td>商品購入代として、上記正に領収いたしました。</td>
        </tr>
        <tr>
            <td>{{ Order.payment_method }}</td>
        </tr>
        </tbody>
    </table>
    <div class="shop-footer">
        <div class="row">
            <div class="col-2">
                <div class="inshi">
                    電子領収書<br>につき<br>印紙不要
                </div>
            </div>
            <div class="col-7">
                {% if BaseInfo.company_name %}
                    {{ BaseInfo.company_name }}<br>
                {% endif %}
                {{ BaseInfo.shop_name }}<br>
                〒100-0001<br>
                東京都千代田区千代田1-1-1<br>
                千代田ビル 12F<br>
                TEL 03-0000-0000<br>
                MAIL xxxxxxxx@example.com
            </div>
        </div>
    </div>

    <div id="print-footer" class="text-center mt-5">
        <button type="button" id="print">プリントアウトする</button>
        <button onclick="window.close();">閉じる</button>
    </div>
</div>
</body>
</html>
  • app/template/default/Mypage/print_invoice.twig (請求書フォーマット)
<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <title>請求書 | {{ BaseInfo.shop_name }}</title>
    <style>
        .container {
            width: 900px !important;
            font-size: 12px;
            margin-top: 20px;
        }

        .logo {
            float: left;
            font-size: 20px;
            font-weight: bold;
        }

        .inkan {
            position: relative;
            padding-right: 80px;
        }

        .inkan-image {
            position: absolute;
            right: 0;
            top: 0;
            z-index: 1;
            width: 70px;
        }

        table thead {
            background-color: #eee !important;
        }

        table tbody {
            border-top: none !important;
        }

    </style>
    <style media="print">
        #print-footer {
            display: none;
        }

        input[type="text"] {
            outline: 0;
            border: none;
        }

        button,
        #edit,
        #mod {
            display: none;
        }
    </style>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script src="{{ asset('assets/js/vendor/printThis.js') }}"></script>
    <script>
        $(function() {

            $('#print').click(function() {
                $('body').printThis({
                    importStyle: true
                });
            });

            $('#edit').click(function() {
                var name = $('.print_name').text();
                var input = $('<input type="text" id="input-name">').val(name);
                $('.print_name').empty().append(input);
                $('#edit').hide();
                $('#mod').show();
            });

            $('#mod').click(function() {
                var newname = $('#input-name').val();
                $('.print_name').empty().text(newname);
                $('#input-name').remove();
                $('#edit').show();
                $('#edit').removeAttr('style');
                $('#mod').hide();
                showEx();
            });

            function showEx() {
                var tmp = $('.print_name').text();

                if (tmp == '') {
                    $('.print_name_ex').hide();
                } else {
                    $('.print_name_ex').show();
                }
            }

        })
    </script>
</head>
<body>
<div class="container">
    <div class="logo">
        {{ BaseInfo.shop_name }}
    </div>
    <div class="text-end">No. {{ Order.order_no }}</div>
    <div class="text-end">発行日 {{ Order.Shippings[0].shipping_date|date_format(null, 'Y年m月d日') }}</div>
    <h1 class="text-center">請求書</h1>
    <div class="row">
        <div class="col-6">
            <h2 id="name"><span class="print_name">{% if Order.company_name %}{{ Order.company_name }} 御中{% else %}{{ Order.name01 }} {{ Order.name02 }} </span><span class="print_name_ex"></span>{% endif %}</h2>
            <button type="button" id="edit">名称変更</button>
            <button type="button" id="mod" style="display: none;">適用</button>
            <div>
                このたびはお買上げいただきありがとうございます。<br>
                下記の内容にて請求させていただきます。
            </div>
        </div>
        <div class="col-6">
            <div class="text-end">
                <div class="inkan">
                    {% if BaseInfo.company_name %}
                        {{ BaseInfo.company_name }}<br>
                    {% endif %}
                    {{ BaseInfo.shop_name }}<br>
                    〒100-0001 東京都千代田区千代田1-1-1<br>
                    千代田ビル 12F<br>
                    TEL 03-0000-0000<br>
                    <img src="{{ asset('assets/img/common/inkan.jpg') }}" alt="" class="inkan-image">
                </div>
            </div>
        </div>
    </div>

    <div class="row my-4">
        <div class="text-center">
            <h4 class="d-inline-block border-bottom pb-2">合計金額(税込) <span class="ms-5">{{ Order.payment_total|price }}</span></h4>
        </div>
    </div>

    <div class="row">
        <div class="col-12">
            下記のとおり納品いたします。
        </div>
    </div>

    <table class="table table-bordered">
        <thead>
        <tr>
            <th class="text-end" style="width:4em">項番</th>
            <th>品名</th>
            <th class="text-end" style="width:4em">数量</th>
            <th class="text-end">単価</th>
            <th class="text-end">金額(税込)</th>
        </tr>
        </thead>
        <tbody>

        {% set index = 1 %}
        {% for OrderItem in Order.MergedProductOrderItems %}
            <tr>
                <td class="text-end">{{ index }}</td>
                <td>
                    {{ OrderItem.product_name }}  {{ OrderItem.classcategory_name1 }}  {{ OrderItem.classcategory_name2 }}
                </td>
                <td class="text-end">{{ OrderItem.quantity|number_format }}</td>
                <td class="text-end">{{ OrderItem.price|price }}</td>
                <td class="text-end">{{ OrderItem.total_price|price }}</td>
            </tr>
            {% set index = index + 1 %}
        {% endfor %}
        <tr>
            <td class="text-end">{{ index }}</td>
            <td>送料</td>
            <td class="text-end">1</td>
            <td class="text-end">{{ Order.delivery_fee_total|price }}</td>
            <td class="text-end">{{ Order.delivery_fee_total|price }}</td>
        </tr>
        {% set index = index + 1 %}
        {% if Order.charge > 0 %}
            <tr>
                <td class="text-end">{{ index }}</td>
                <td>手数料</td>
                <td class="text-end">1</td>
                <td class="text-end">{{ Order.charge|price }}</td>
                <td class="text-end">{{ Order.charge|price }}</td>
            </tr>
            {% set index = index + 1 %}
        {% endif %}
        {% for OrderItem in Order.OrderItems %}
            {% if OrderItem.order_item_type_id == 4 %}
                <tr>
                    <td class="text-end">{{ index }}</td>
                    <td>{{ OrderItem.product_name }}</td>
                    <td class="text-end">1</td>
                    <td class="text-end">{{ OrderItem.priceIncTax|price }}</td>
                    <td class="text-end">{{ OrderItem.total_price|price }}</td>
                </tr>
                {% set index = index + 1 %}
            {% endif %}
        {% endfor %}

        <tr>
            <td colspan="3" rowspan="3"></td>
            <td class="text-center">合計</td>
            <td class="text-end h5">{{ Order.payment_total|price }}</td>
        </tr>

        </tbody>
    </table>

    {% if Order.note %}
        <table class="table table-bordered">
            <thead>
            <tr>
                <th>備考</th>
            </tr>
            </thead>
            <tbody>
            <tr>
                <td>{{ Order.note|nl2br }}</td>
            </tr>
            </tbody>
        </table>
    {% endif %}
    <div class="row">
        <div class="col-8">
            【お振込先】<br>
            XXXX銀行 XXXX支店 普通 9999999 XXXX株式会社<br>
            振込手数料は御社ご負担でお願い申し上げます。
        </div>
        <div class="col-4 text-end">
            ご注文商品についてのお問い合わせ・ご意見はこちら<br>
            TEL: {{ BaseInfo.phone_number }}|{{ BaseInfo.business_hour }}
        </div>
    </div>

    <div id="print-footer" class="text-center mt-5">
        <button type="button" id="print">プリントアウトする</button>
        <button onclick="window.close();">閉じる</button>
    </div>
</div>
</body>
</html>

領収書、請求書ともに宛先名も変更できるようにしています。

印刷用ライブラリの導入

画面から簡単に印刷できるようにするため、下記ライブラリを導入します。

jasonday.github.io

こちらのライブラリをダウンロード後、[EC-CUBE ROOT]/html/template/default/assets/js/vendor/printThis.js へ配置してください。

印刷用関数の作成

印刷画面を表示させるためにカスタマイズを行います。

MypageControllerクラスを変更します。変更内容ですが、印刷するための画面を表示させるための関数を追加するだけです。

  • src/Eccube/Service/MypageController.php
/**
 * 印刷画面を表示する.
 *
 * @Route("/mypage/{order_no}/{print}/print", name="mypage_print")
 *
 * @param Request $request
 * @param $order_no
 * @param $print
 * @return \Symfony\Component\HttpFoundation\Response
 */
public function exportPrint(Request $request, $order_no, $print)
{
    $Customer = $this->getUser();

    /* @var $Order \Eccube\Entity\Order */
    $Order = $this->orderRepository->findOneBy(
        [
            'order_no' => $order_no,
            'Customer' => $Customer,
        ]
    );

    if (!$Order) {
        throw new NotFoundHttpException();
    }

    return $this->render('Mypage/print_'.$print.'.twig', [
        'Order' => $Order,
    ]);
}

印刷用ボタンを表示

マイページの購入詳細画面で印刷用ボタンを表示させます。既存のファイルに対して修正しても良いのですが、普段修正する場合、app/template/default直下にコピーしてから修正することが多いので、今回もそのようにします。

src/Eccube/Resource/template/default/Mypage/history.twigファイルをapp/template/default/default/Mypage/history.twigへコピー後に以下の内容を追加します。

  • app/template/default/default/Mypage/history.twig
<a href="{{ url('mypage_print', {'order_no': Order.order_no, 'print': 'receipt'}) }}" class="ec-inlineBtn" target="_blank">領収書</a>
<a href="{{ url('mypage_print', {'order_no': Order.order_no, 'print': 'invoice'}) }}" class="ec-inlineBtn" target="_blank">請求書</a>

上記内容は表示させたい位置へ追加してください。

以上で表示可能となります。

それぞれ、「発送済み」の時にしか表示させたくないのであれば、以下のように変更してください。

  • app/template/default/default/Mypage/history.twig
{% if Order.OrderStatus.id == constant('Eccube\\Entity\\Master\\OrderStatus::DELIVERED') %}
    <a href="{{ url('mypage_print', {'order_no': Order.order_no, 'print': 'receipt'}) }}" class="ec-inlineBtn" target="_blank">領収書</a>
    <a href="{{ url('mypage_print', {'order_no': Order.order_no, 'print': 'invoice'}) }}" class="ec-inlineBtn" target="_blank">請求書</a>
{% endif %}

また、一度領収書ボタンが押されたらAmazonのように「再発行」を表示させたい場合、DBを変更する必要があります。
その方法は今回割愛しますが、必要な方はコメントに欲しいと記述してください。

なお、領収書については税込5万円を超えた場合、収入印紙が必要じゃないのかと聞かれることが多いのですが、 今回の方法は電子書類扱いとなるため非課税扱いとなります。 そのため、領収書ファイルをメール、FAXなどで送った場合などは印紙が不要です。
電子領収書をプリントアウトした場合も、そこに印鑑を押印しない限りは非課税扱いですので収入印紙は不要となります。

以上で領収書と請求書の作成方法となりますので必要な方は参考にしてください。