2021年12月20日月曜日

やるぞ!青色申告2021で印刷できない

パソコンにあまり詳しくない方からの連絡で、『やるぞ!青色申告』というアプリで印刷ができない!
という相談を受けまして、出張サービスに行ってきました
環境は Windows 10 64bit、『やるぞ!青色申告2021』がインストールされているパソコンです。
身内からのご相談なので、無償対応ですw

『やるぞ!青色申告』ってのは市販の申告用アプリで、こういっちゃなんだけどw使い勝手は良くないと感じますがw
値段が安いので使っている方も多いようですね。

さっそく起動して、「印刷」メニューから「元帳」を選んで、一部だけ印刷しようとすると

Acrobat Reader がインストールされていません

というエラーで、たしかに印刷できない。

ん? Acrobat Reader ならインストールされてるぞ・・・デスクトップにアイコンもあるしちゃんと起動もする。
おかしいな・・・
念のため、もう一回インストールしてみるか
ということで Adobe サイトから Acrobat Reader を再度ダウンロードしてインストーラを起動してみた。

すでにインストールされています。

なぜじゃ?
ということで、いろいろググッてみたところ、どうやら Acrobat Reader の 32bit版でなければならないらしいことをキャッチ。
Acrobat Reader を変える必要があるので、まず、インストールされている 64bit版の Acrobat Reader をアンインストールします。
Windows10 の「設定」メニューを開き、「アプリと機能」で「Adobe Acrobat Reader DC」をアンインストールします。


さて、32bit版のインストールです。
さっそく Acrobat Reader のダウンロードページを開きます。
https://get.adobe.com/jp/reader/
このページですね。


毎度インストールしてこようとする McAfee は不要なのでチェックボックスをOFFにしておきます。
しかーし。このままダウンロードすると、64bit Windows10 環境なので、64bit版がインストールされてしまいます。

32bit版をインストールするには、下のほうにある『別の言語版または別のオペレーティングシステムをお持ちですか?』のリンクをクリックします。
でもって、
手順1で「Windows 10」を選択
手順2で「Japanese」(日本語)を選択
手順3で「Reader DC xxxxxxxxxxx_xxxxxxxx Japanese for Windows」の64bitと書かれていない方を選択
不要な「McAfee」のチェックを外して「Acrobat Reader をダウンロード」を選択します。

あとは、インストールして再起動です。
『やるぞ!青色申告2021』を起動して、「印刷」メニューから「元帳」を選んで印刷してみました。
無事 Acrobat Reader に元帳が表示されて印刷できるようになりました
めでたしめでたし。

出張先の環境では、実際には最初は空白のページが Acrobat Reader に表示されていました。
何度か別の元帳を印刷しようとしたりしていたら、表示されるようになりました。
この回避策、パソコンにそれほど詳しくない人には対応できそうにないなーと思います。
開発元が対応すべきですよね!w

2021年12月6日月曜日

郵便番号から自動的に住所入力させる

cakePHP3 + bootstrap5 プロジェクトで郵便番号入力から自動的に住所欄を入力できるようにしてみました。

ふだん、css/jsはバージョンによって動きが変わったりしないよう、サーバ上にコピーして利用するようにしてますが 市区町村合併などで郵便番号が変化する場合があるので、こちらはgithubのサイトを参照するようにしてます。

[slip.ctp]

≤?php

$this->Html->script("//yubinbango.github.io/yubinbango/yubinbango.js", ['block' => true]);

?>

≤div class="col-md-6 h-adr">
    ≤span class="p-country-name" style="display:none;">Japan≤/span>
    ≤table class="table_client_info as_bd_cl">
        ≤tr>
            ≤th>≤input type="button" class="form-control as_button" value="着住所">≤/th>
            ≤th style="text-align: right;">郵便番号≤/th>
            ≤td>
                ≤input type="text" class="form-control p-postal-code" placeholder="郵便番号を入力" />
            ≤/td>
        ≤/tr>
        ≤tr>
            ≤th>住所1≤/th>
            ≤td colspan="2">
                ≤input type="text" class="form-control p-region p-locality p-street-address" placeholder="住所を入力" />
            ≤/td>
        ≤/tr>
        ≤tr>
            ≤th>住所2≤/th>
            ≤td colspan="2">
                ≤input type="text" class="form-control p-extended-address" placeholder="建物名、部屋番号を入力" />
            ≤/td>
        ≤/tr>

cssクラス部分は、サイズとか罫線を設定してます。
いやー。しかし、こりゃ便利だw
めでたしめでたし。

【追記】
最近になって、郵便番号での住所入力が必要になって、自分のブログを参照にしてみたら、なかなか動いてくれなかった(笑)
原因は、class="h-adr" を付加してなかったからでした。
上記の例では上部の div タグに設定してますね。
ここ、忘れないようにしてくださいw

cakePHP3:javascript 3桁単位でカンマを表示する数字入力

cakePHP3.9 + bootstrap5 の環境で金額入力のために3桁単位で自動で数字を入力できるようにしようとしたんですね。

cleave.js ってのが便利そうだったので使ってみました。

参考
フォームのinput要素に電話番号・日付・時間・金額など、数字のフォーマットを定義できるスクリプト -Cleave.js
https://coliss.com/articles/build-websites/operation/javascript/format-input-text-content-cleave.html

cleave.js
https://github.com/nosir/cleave.js

上記のサイトなどを参考にして、次のようなコードを javascript に記述してました。
[project.js]

    var cleave = new Cleave('.input-decimal', {
      numeral: true,
      numeralThousandsGroupStyle: 'thousand'
    });

[project.css]

    .input-numeral {
        text-align: right;
    }

[slip.ctp]

    ≤div class="row">
        ≤h6 class="col-4 d-flex align-items-center">小計≤/h6>
        ≤div class="col-8">≤input type="text" class="form-control input-numeral input-decimal" value="10,000" />≤/div>
    ≤/div>
    ≤div class="row">
        ≤h6 class="col-4 d-flex align-items-center">消費税≤/h6>
        ≤div class="col-8">≤input type="text" class="form-control input-numeral input-decimal" value="1,000" />≤/div>
    ≤/div>
    ≤div class="row">
        ≤h6 class="col-4 d-flex align-items-center">合計≤/h6>
        ≤div class="col-8">≤input type="text" class="form-control input-numeral input-decimal" value="11,000" />≤/div>
    ≤/div>

困ったことに、数字入力のinputタグが複数あると、上手く動いてくれなかったんです。
chrome の開発者ツールでは、以下のようなメッセージが出てしまいました。

cleave.min.js:8 [cleave.js] Multiple input fields matched, cleave.js will only take the first one.

いやいや、伝票入力画面に数値入力が一か所しかないなんてありえないでしょ!
ということで調べた結果、javascript を次のように変更しました。

[project.js]

    $('.input-decimal').toArray().forEach(function(field){
      new Cleave(field, {
        numeral: true,
        numeralThousandsGroupStyle: 'thousand'
      });
    });

伝票用のフォーム画面では、金額入力フィールドを動的に追加できるようにしましたが、追加の都度 new Cleaveするようにして、ちゃんと動いてくれました。
めでたしめでたし。

PHP ip2longの罠(2) ローカルIPアドレスが '::1'

XAMPPを使った cakePHP3のローカルな開発環境でブラウザ表示させる際に、$_SERVER['REMOTE_ADDR']を使ってIPアドレスを取得すると、localhostでは '::1' という値が返されてきた。

プログラムでは ip2long()関数を使って数値化されたIPアドレス値をデータベースに照合しているので、これじゃ使えない。

  $ip_address = $_SERVER['REMOTE_ADDR'];
  $ip_value = ip2long($ip_address);

実際、$ip_address='::1' の場合に $ip_value=ip2long('::1');を実行すると false が返ってきてしまっている。

WEBを検索すると、次の情報があったのでやってみた。(コントローラ内での呼び出し)

  $ip_address = $this->request->clientIp();

これでも、値は '::1' となる。こまったぞ・・・

よくよく考えると、自分のIPを返せばいいわけで。
次のコードでうまくいくようになった。

if($_SERVER["HTTP_HOST"] == "localhost")
{
	$ip_address = getHostByName(getHostName());
}
$ip_value = ip2long($ip_address);

無事取得できました。
めでたしめでたし。

2021年11月8日月曜日

PHP ip2longの罠

cakePHPのサーバー運用も多くやってますが、もっぱら cakePHP3.9 で作業することが多いです。
担当外の cakePHP1.2で構築されたサーバーくんのトラブルに駆り出されてみたので、そのお話です。

機器との接続がうまく行かないと言われましてですね。
機器は MySQLのテーブルに IPアドレス が記録されていまして、それを読み込んでアクセスしているわけです。

php には、ip2long(), long2ip() という関数が用意されていて、IPアドレスを整数値に変換したり逆に整数値をIPアドレスの文字列に変換できたりするわけで
データベースに文字列でIPアドレスを格納するよりも数値で格納しておく方が効果的なので、よく使われます。

たとえば、192.168.3.20 というIPアドレスを変換してDBに格納する場合は Windows10標準の電卓でHEX入力するといい。
192=C0, 168=A8, 20=14 なので、HEX(16進数)入力で C0A80314 と入力すると、DEC(10進数)部分に 3,232,236,308 が表示される。
DBには、この値 3232236308 を入れてしまえばいいわけ。

しかしだ。
動いてくれないのだよ。
なぜじゃ!?

ローカルに環境を構築する暇なんてなかったので、なかなかデバッグしづらい環境だったけど、原因を突き止めました。


Windows電卓は64bit演算なのですよ。
ターゲットの環境は32bit環境。

2148483647(0x80000000)以上の数値は long なので負数になるべきなのだYO!

ということで、2の補数を使って負数に変換しなきゃいけない。
64bit環境上に、こんなプログラムを作ってみました。

<?php
/*
    ip2long 32bit 処理
*/
$half = ip2long('128.0.0.0');   // 2148483647

$msg = '';
if(isset($_GET['ip']))
{
    $ip = $_GET['ip'];
    $long = ip2long($ip);
    if($half <= $long)
    {
        //MSB が1
        $long = ($long ^ (16 ** 8-1)) + 1;
        $long = 0 - $long;
    }
    $msg .= 'ip:' . $ip . ' value:' . $long;
}
?>

<HTML>
<BODY>
    <DIV>
        <?= $msg ?>
    </DIV>
</BODY>
</HTML>

https://seasoft.co.jp/bin/ip2long32.php で実際に動作できます。
2の補数なんて久しぶりに使ったから、ちょっと時間かかちゃったよ笑


めでたし、めでたし。

A4横 CSS(スタイルシート) の landscape がうまく行かない

「A4横」左図の帳票レイアウトを作成して印刷しようとしたら、横幅が狭く表示されてうまく行きませんでした。

WEBサイトを検索すると、あちこちにこうすればできると書いてある。

ちなみに、bootstrap5 を使ってレイアウトしてます(帳票なのでレスポンシブは考慮してない)

@page {
    size: A4 landscape; 
    size: 297mm 210mm;
    margin: 0mm;
}

A4横に、なるにはなるんだけど、コンテンツの横幅がどう見ても狭い。
A4縦の時の横幅を超えない(程度の)横幅でしか印刷されない・・・・
なぜじゃ!?

いろいろ調べたり、調べてもらったりして bootstrap がまずいことがわかりました。
(Hさん、ご協力、ありがと。)

bootstrap の次の部分が影響してましたよ、と。

.container, .container-lg, .container-md, .container-sm{
 max-width: 960px;
}

以下のようにスタイルシートを追記すると、ちゃんとA4横で印刷されるようになりました。

@media print {
    .container {
         max-width: 297mm !important;
    }
}


めでたし、めでたし。

2021年10月21日木曜日

Ajax post で WEB-API 呼び出そうとしたらパラメータが渡せなくて少し悩みました。

Javascriptのコード(抜粋)

	var params = [];
	params['date'] = '2021-10-21';
	var url = 'APIのURL';

	$.post({
	      type: 'POST', 
	      url: url,
	      dataType: 'json',
	      data: params,
	      cache: false,
	    }).done(function(response){
	        /*do someting*/
	    }).fail(function(XMLHttpRequest, textStatus, error){
	        /*do someting*/
	    }).always(function(data){
	        /*do someting*/
	    });

受け口となるAPIのコード(cakePHP3.x の controller)

    public function index() 
    {
        $date = $this->request->getData('date');
        ;//以下略

XAMPP+VS Code 環境でブレイクポイントを貼って $date 変数の中身を見ると
からっぽ!
なぜじゃぁぁぁぁ!。

ということで、小一時間悩みました。
わかる人にはすぐわかるのかもしれませんが、この何にも警告を出してくれないトラップは時間かかってしまうことがありますね~。


間違っているのは javascript 抜粋の1行目。

	var params = [];

ここです!

正しくは

	var params = {};

これだけ。 わすれないように、メモ。っと。

2021年9月17日金曜日

cakePHP で開発環境とレンタルサーバでデータベースを日本時間にする

 cakePHP3.9 をXAMPP上で開発しているプロジェクトをレンタルサーバ(XSERVER)上で動作させようとしたら、データベースの日本時間設定がうまくいかなかったので、まとめてみます。

まず、あちこちのサイトに書いてあるこの方法。

config/app.php

'App' => [
   ;
        'defaultTimezone' => env('APP_DEFAULT_TIMEZONE', 'Asia/Tokyo'),
   ;
'Datasources' => [
        'default' => [
                ;
            'timezone' => 'Asia/Tokyo',
                ;

この方法では XAMPP上のプロジェクトはちゃんと動作するけど、XSERVERで実行すると
Unknown or incorrect time zone: 'Asia/Tokyo'
というエラーが発生して、動作しません。

別のWEBサイトでの方法では、'Asia/Tokyo' の代わりに 'JST' を指定すればOK
という情報もありましたが・・・
Unknown or incorrect time zone: 'JST'
XSERVERでは動きませんでしたね。

MySQL の mysql テーブル timezoneをインポートして設定するといいのでしょうけれど
XSERVERでそれをやる方法がわかりませんでした

で、最終的にうまくいった方法を披露しておきます。

'App' => [
   ;
        'defaultTimezone' => env('APP_DEFAULT_TIMEZONE', 'Asia/Tokyo'),
   ;
'Datasources' => [
        'default' => [
                ;
            'timezone' => '+9:00',
                ;

Datasources側だけ、timezoneを '+9:00' に設定するわけです。
これでデータベース内の記録日時とWEBサイトで表示される日時を同期させることができました。
めでたし、めでたし。

2021年1月11日月曜日

jquery VIDEOタグでsrcを変更すると再生できなかった。

 HTML の VIDEOタグを用意して、アクションで自動的にビデオを再生するようにしてみましたら、「再生できません」というエラーで困っちゃいました。

HTML(抜粋)

<div id="div_video">
  <video id="id_video" poster="poster.gif" autoplay loop muted></video>
</div>

javascript(抜粋)

<script type="text/javascript" src="/js/jquery.min.js"></script>
<script type="text/javascript">
function play_video() {

    $('#id_video').attr('src', "video.mp4");
    $('#id_video').get(0).play();
}
</script>

こんな感じです。window.onload() でやると再生できるんですが、アクション後に実行すると poster.gif は表示されますが、ビデオ再生はうまくいきませんでした。

開発者ツールで $('#id_video').attr('src') を見ると、予定されていない内容が入ってます。

video タグでは <source></source>タグを使って複数のファイルを指定できますので get(0) がおかしいのだと思われます。

今回の再生は複数ファイルは必要ないので、get(0) で正しく処理できるよう、次のように記述しました。

function play_video() {
    $('#div_video').children().remove();
    $('#div_video').append('<video id="id_video" autoplay loop muted></video>');
    var target = $('#id_video');
    target.attr("poster",  "poster.gif");
    target.attr("src", "video.mp4");
    target.get(0).play();
}

毎回 <video>タグを作り直すことで get(0) が正しく取得できるようになります。

※ここでは書きませんが、上記アクション実行の前に、前回再生中のタグがある場合は再生を止める必要があるみたいです。(リークしてしまうことがあるのかな?)

※<video> の属性に 'muted' を付けているのは、Chrome でデバッグするとエラーになっちゃうからです。