サブテーブルの条件付き必須項目設定について

JS初心者です…

現金精算テーブル内にある数値フィールド「現金_金額」に0以外の数字が入っていたら、添付フィールド「現金_領収書」を必須項目にしたいです!(カンパニーカードテーブルも同様に)

AIに下記のコードを作ってもらったのですが、保存できなかったりとうまくいかず…

コミュニティー内を探してもピンとくるようなスレッドを見つけられませんでした…

添付フィールドの条件付き必須項目設定は難しいのでしょうか?ご教示いただけますと幸いです…!よろしくお願いします:woman_bowing:

コードをここに入力// 即時関数(IIFE)でコード全体を囲むことで、他のJavaScriptファイルとの変数名の衝突を防ぐ、大切なおまじないです。
(function() {
  'use strict'; // プログラムのエラーをチェックしやすくするための、おまじない(厳格モード宣言)です。

  // --------------------------------------------------------------------------------
  // 設定エリア:君の新しいルールに合わせて、ここを書き換えたよ!
  // --------------------------------------------------------------------------------

  // ▼▼▼ 1つ目のテーブル(現金精算) ▼▼▼
  const CASH_TABLE = '現金精算';
  const CASH_AMOUNT = '現金_金額'; // ★きっかけを「日付」から「金額」に変更!
  const CASH_ATTACHMENT = '現金_領収書';

  // ▼▼▼ 2つ目のテーブル(カンパニーカード精算) ▼▼▼
  const CARD_TABLE = 'カンパニーカード精算';
  const CARD_AMOUNT = 'カード_金額'; // ★きっかけを「日付」から「金額」に変更!
  const CARD_ATTACHMENT = 'カード_領収書';


  /**
   * 必須項目の「見た目」(赤い*マーク)と「エラー表示」(赤枠)を更新するための関数です。
   * @param {object} record - kintoneのレコードオブジェクト
   */
  function updateFieldState(record) {
    
    // 1. 現金精算テーブルを処理
    if (record[CASH_TABLE] && record[CASH_TABLE].value) {
      record[CASH_TABLE].value.forEach((row) => {
        if (!row.value[CASH_AMOUNT] || !row.value[CASH_ATTACHMENT]) return;
        
        const amountValue = row.value[CASH_AMOUNT].value; // ★日付(dateValue)から金額(amountValue)に変更
        const attachmentValue = row.value[CASH_ATTACHMENT].value;
        
        // ★金額に値があれば(0や空欄でなければ)、添付を必須(true)に
        const isRequired = !!amountValue; 
        row.value[CASH_ATTACHMENT].required = isRequired;
        
        if (isRequired && attachmentValue.length === 0) {
          row.value[CASH_ATTACHMENT].error = '必須です。';
        } else {
          row.value[CASH_ATTACHMENT].error = null;
        }
      });
    }
    // 2. カンパニーカード精算テーブルを処理
    if (record[CARD_TABLE] && record[CARD_TABLE].value) {
      record[CARD_TABLE].value.forEach((row) => {
        if (!row.value[CARD_AMOUNT] || !row.value[CARD_ATTACHMENT]) return;

        const amountValue = row.value[CARD_AMOUNT].value; // ★日付(dateValue)から金額(amountValue)に変更
        const attachmentValue = row.value[CARD_ATTACHMENT].value;
        
        const isRequired = !!amountValue; 
        row.value[CARD_ATTACHMENT].required = isRequired;
        
        if (isRequired && attachmentValue.length === 0) {
          row.value[CARD_ATTACHMENT].error = '必須です。';
        } else {
          row.value[CARD_ATTACHMENT].error = null;
        }
      });
    }
  }

  /**
   * テーブルが表示されるまで待ち、その後「変化」をずっと見張る監視員を配置する関数
   * @param {string} tableCode - 監視対象のテーブルのフィールドコード
   */
  function observeTable(tableCode) {
    const tableElement = kintone.app.record.getFieldElement(tableCode);
    if (!tableElement) {
      setTimeout(() => observeTable(tableCode), 100);
      return;
    }
    // テーブルが見つかったら、監視員(MutationObserver)を作成
    const observer = new MutationObserver(() => {
      // 監視中にテーブルで何か変化があったら(他のJSの再描画など)、
      // もう一度、必須チェックの「見た目」を更新する!
      // ★保存時のエラーを防ぐため、kintone.app.record.get() を使って最新のレコード情報を取得
      const currentRecord = kintone.app.record.get();
      if (currentRecord) {
        updateFieldState(currentRecord.record);
      }
    });
    // 監視員に、テーブルを厳しく監視するように命令
    observer.observe(tableElement, {
      childList: true, // 行の追加/削除などを監視
      subtree: true    // テーブル内のあらゆる変化を監視
    });
  }

  // --------------------------------------------------------------------------------
  // メイン処理(イベントハンドラの設定)
  // --------------------------------------------------------------------------------

  // 1. 画面表示時と、テーブル・「金額」・添付ファイルが変更された時
  const uiEvents = [
    'app.record.create.show', 'app.record.edit.show',
    'app.record.create.change.' + CASH_TABLE, 'app.record.edit.change.' + CASH_TABLE,
    'app.record.create.change.' + CASH_AMOUNT, // ★「日付」から「金額」に変更
    'app.record.edit.change.' + CASH_AMOUNT,
    'app.record.create.change.' + CARD_TABLE, 'app.record.edit.change.' + CARD_TABLE,
    'app.record.create.change.' + CARD_AMOUNT, // ★「日付」から「金額」に変更
    'app.record.edit.change.' + CARD_AMOUNT,
    'app.record.create.change.' + CASH_ATTACHMENT, 'app.record.edit.change.' + CASH_ATTACHMENT,
    'app.record.create.change.' + CARD_ATTACHMENT, 'app.record.edit.change.' + CARD_ATTACHMENT
  ];

  kintone.events.on(uiEvents, (event) => {
    // ★ 画面が開かれたら、監視員を配置する
    if (event.type.endsWith('.show')) {
      observeTable(CASH_TABLE);
      observeTable(CARD_TABLE);
    }
    // ★ kintoneのイベント(event.record)を使って、即座に見た目を更新
    updateFieldState(event.record);
    return event;
  });

  // 2. 保存ボタンが押された時
  const saveEvents = [
    'app.record.create.submit',
    'app.record.edit.submit'
  ];

  kintone.events.on(saveEvents, (event) => {
    
    // ★★★
    // 保存時も、kintoneが持っている古いデータ(event.record)ではなく、
    // 「今、画面に表示されている本当の最新データ」を使ってチェックする!
    const currentRecord = kintone.app.record.get().record;
    let errorMessage = '';
    
    // 現金精算テーブルをチェック
    currentRecord[CASH_TABLE].value.forEach((row, index) => {
      const amountValue = row.value[CASH_AMOUNT].value; // ★「金額」をチェック
      const attachmentValue = row.value[CASH_ATTACHMENT].value;
      
      // エラーをセットするのは、kintoneに渡す「event.record」に対して行う
      const eventRow = event.record[CASH_TABLE].value[index];
      if (eventRow.value[CASH_ATTACHMENT]) {
        eventRow.value[CASH_ATTACHMENT].error = null;
      }

      // ★「金額」に値があるのに、添付が0件だったら
      if (amountValue && attachmentValue.length === 0) { 
        errorMessage += `現金精算テーブルの ${index + 1} 行目: 領収書が添付されていません。\n`;
        if (eventRow.value[CASH_ATTACHMENT]) {
          eventRow.value[CASH_ATTACHMENT].error = '必須です。';
        }
      }
    });
    
    // カンパニーカード精算テーブルをチェック
    currentRecord[CARD_TABLE].value.forEach((row, index) => {
      const amountValue = row.value[CARD_AMOUNT].value; // ★「金額」をチェック
      const attachmentValue = row.value[CARD_ATTACHMENT].value;
      
      const eventRow = event.record[CARD_TABLE].value[index];
      if (eventRow.value[CARD_ATTACHMENT]) {
        eventRow.value[CARD_ATTACHMENT].error = null;
      }

      // ★「金額」に値があるのに、添付が0件だったら
      if (amountValue && attachmentValue.length === 0) {
        errorMessage += `カンパニーカード精算テーブルの ${index + 1} 行目: 領収書が添付されていません。\n`;
        if (eventRow.value[CARD_ATTACHMENT]) {
          eventRow.value[CARD_ATTACHMENT].error = '必須です。';
        }
      }
    });

    // 3. エラーが一つでもあったら、保存を止める
    if (errorMessage) {
      const errorTitle = '必須項目に未入力があります。赤くなっている項目を確認してください。';
      event.error = errorTitle;
      
      if (typeof Swal !== 'undefined') {
        Swal.fire({
          icon: 'error',
          title: errorTitle,
          html: `<pre style="text-align: left; font-size: 14px;">${errorMessage}</pre>`
        });
      } else {
        alert(errorTitle + '\n\n' + errorMessage);
      }
      return Promise.reject(event);
    }
    
    return event;
  });

})();または貼り付け

こんにちは!
ちなみになのですが、どんな処理の流れで添付ファイルを必須扱いにしようとしている感じでしょうか??

保存前のイベントで、「現金_金額」に0以外の数字が入っていて、添付ファイル空っぽだったら保存処理に進まないようにするとかで実現できるのでは!?と思います。

1 Like

こんにちは!ありがとうございます!

1レコード内に4つのテーブルがあり、1ユーザーごとに1ヶ月分の精算を各テーブルに入力をしてもらうアプリを作成しています。

現金での支払が発生したら現金精算のテーブルに金額を入力する。その時に金額の入力があったら領収書の添付漏れがないように必須にしたい感じです…!

1ヶ月の間に、現金を使わなかった場合もあるので、「添付ファイルが空だったら保存処理に進まないようにする」が難しく…。なので「金額に数字が入っていたら必須項目にする」という考えになりました。

他に良い条件などあればよのですが、頭が凝り固まってしまい今のところ思いつかずです…

発想の転換・・・
領収書を添付後に金額が入力可能になるようなロジックではダメでしょうか?
(=添付保存すると「金額」フィールドの グレーアウト が解除される)

但し、「添付」:right_arrow:「保存」:right_arrow:「編集」:right_arrow:「金額入力」:right_arrow:「保存」
と、保存釦を2回押下げ する必要があります

また、複数行同時の入力も出来ないです

Boost! Style と云うPluginを利用して検証してみました

※検証条件=添付ファイル名には【 領収書_ 】が入っていること =LIKE(領収書_ )

ご参考まで!

添付した検証GIFアニメの、Pluginの設定画面が
ご入用の際はコメント頂ければ 作成いたします(貼ります)

添付ファイルを先に保存

2 Likes

ありがとうございます!なるほどな発想の転換でした…!

今回のアプリ作成の要望者に提案したところ、運用上で入力工数削減を目標としているため、クリック数を少なくしたい、もう少し良い方法はないかと渋られてしまいました…

このロジックで運用できるルールを設けたうえでアプリを作成してみるのもありだなと思いました…!個人的にですが試してみます…!

1 Like

返信、遅くなってスミマセン:person_bowing:

ま~~そうでしょうね、自分だって面倒っくさいナ~~って思いました

操作感? 使用感を試すなら、わざわざコードを書くのは面倒じゃないですか?
Boost! Style での設定画面を添付してみました ご参考まで!:blush:
(3ヶ月間は無料だし、以降もたまに ライセンス購入を促すメッセージが表示されますが
そのまま利用できます…ライセンス料もめっちゃ安いですけどね)

2 Likes

いえいえ…!お返事ありがとうございます!

Boost! Styleの設定画面ありがとうございます!こちらで設定して試してみます!お安いので、あわよくばライセンスもゲットしてもらうよう社内稟議上げてみます!笑

2 Likes

このトピックはベストアンサーに選ばれた返信から 3 日が経過したので自動的にクローズされました。新たに返信することはできません。