0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【kintone】採番

0
Last updated at Posted at 2025-11-14

標準機能で採番

アプリコードを有効にすると、レコード番号が採番っぽくなる。

公式プラグインで採番

簡易な形式の採番なら自動採番プラグインでできる。

ファイル読み込みするときに採番

イベントファイル読み込みを起点とするものがないため
kintone単独での自動化は無理だと思われる。

外部システムと連携するのも手間なので
Excelの関数やオートフィルで採番してしまうのが現実的か。

レコード追加の日時 + 作成者のログイン名

タイトルなし.png
2025年11月14日9時18分7秒に、ログイン名 000001 のユーザーが
レコード追加すると、上図のように採番される。

前提としてLuxonが必要。

(() => {
  'use strict';

  // 編集可能な全ての画面で[採番]を編集不可にする
  const disableEvents = [
    'app.record.create.show',
    'app.record.edit.show',
    'app.record.index.edit.show'
  ];

  kintone.events.on(disableEvents, (event) => {
    const record = event.record;
    record.採番.disabled = true;
    
    return event;
  });

  // レコード追加画面で保存するときの処理
  kintone.events.on('app.record.create.submit', (event) => {
    const record = event.record;
    
    // ログインユーザーのログイン名を取得
    const loginName = kintone.getLoginUser().code;
    
    // 現在日時を年~秒の数字14桁にフォーマット
    const dateStr = luxon.DateTime.now().toFormat('yyyyMMddHHmmss');
    
    // 「yyyyMMddHHmmss-ログイン名」の形式で[採番]に値を設定
    record.採番.value = `${dateStr}-${loginName}`;
    
    return event;
  });

})();

期数 + 連番

2001-10-01 から 2002-09-30 を第1期として、日付 の値から期数を算出する。

例えば、既存レコードの 採番 の値に 25-001 25-002 25-003 25-004 があるとき
日付 に第25期の年月日(2025-10-01 ~ 2026-09-30)を入力してレコード追加すると
25-005 と採番される。

前提として Luxon と kintone REST API Client が必要。

(() => {
  'use strict';

  // 編集可能な全ての画面で[採番]を編集不可にする
  const disableEvents = [
    'app.record.create.show',
    'app.record.edit.show',
    'app.record.index.edit.show'
  ];

  kintone.events.on(disableEvents, (event) => {
    event.record.採番.disabled = true;
    return event;
  });

  // レコード追加画面で保存するときの処理
  kintone.events.on('app.record.create.submit', async (event) => {
    const record = event.record;
    
    try {
      // [日付]から期数を計算(2001年10月1日を第1期の始点とする)
      const dateValue = record.日付?.value;
      const dt = luxon.DateTime.fromISO(dateValue);
      const fiscalYear = dt.month >= 10 ? dt.year : dt.year - 1;
      const kisu = fiscalYear - 2001 + 1;
      
      if (kisu < 1) {
        event.error = '日付が2001年10月1日以前です。期数を計算できません。';
        return event;
      }

      const client = new KintoneRestAPIClient();
      const appId = kintone.app.getId();
      
      // 全ての既存レコードを取得
      const records = await client.record.getAllRecordsWithCursor({
        app: appId,
        fields: ['採番']
      }).catch(() => []);
      
      // 同じ期数の最大連番を取得
      const maxNumber = records.reduce((max, rec) => {
        const saibanValue = rec.採番?.value;
        if (!saibanValue) return max;
        
        const parts = saibanValue.split('-');
        
        if (parts.length === 2 && parts[0] === String(kisu)) {
          const num = parseInt(parts[1], 10);
          return isNaN(num) ? max : Math.max(max, num);
        }
        return max;
      }, 0);
      
      // 新しい連番を生成(3桁ゼロパディング)
      const newNumber = (maxNumber + 1).toString().padStart(3, '0');
      
      // [採番]に値を設定
      record.採番.value = `${kisu}-${newNumber}`;

      return event;
      
    } catch (error) {
      console.error('採番エラー:', error);
      event.error = '採番処理でエラーが発生しました。';
      return event;
    }
  });

})();

組織ID + 連番

ユーザー選択 に入力されたユーザーが属する組織の組織IDを使用する。
前提として kintone REST API Client が必要。

(() => {
  'use strict';
  // 編集可能な全ての画面で[採番]を編集不可にする
  const disableEvents = [
    'app.record.create.show',
    'app.record.edit.show',
    'app.record.index.edit.show'
  ];
  kintone.events.on(disableEvents, (event) => {
    event.record.採番.disabled = true;
    return event;
  });
  // レコード追加画面で保存するときの処理
  kintone.events.on('app.record.create.submit', async (event) => {
    const record = event.record;
    try {
      // [ユーザー選択]の値からログイン名を取得
      const selectedUser = record.ユーザー選択.value;
      
      let orgId;
      
      // ユーザー選択が空の場合は00を組織IDとする
      if (!selectedUser || selectedUser.length === 0) {
        orgId = '00';
      } else {
        const userCode = selectedUser[0].code;
        // ログイン名からユーザー情報を取得
        const userResp = await kintone.api(kintone.api.url('/v1/users.json', true), 'GET', {
          codes: [userCode]
        });
        // ユーザー情報から所属する組織の組織IDを取得
        const primaryOrgCode = userResp.users[0].primaryOrganization;
        
        // 組織に所属していない場合も00を組織IDとする
        if (!primaryOrgCode) {
          orgId = '00';
        } else {
          // 組織IDを2桁ゼロパディング
          orgId = String(primaryOrgCode).padStart(2, '0');
        }
      }
      
      const client = new KintoneRestAPIClient();
      
      // 現在のアプリのアプリIDを取得
      const appId = kintone.app.getId();
      
      // [採番]の値が同じ組織IDで始まるレコードを取得
      const query = `採番 like "${orgId}-"`;
      const allRecords = await client.record.getAllRecordsWithCursor({
        app: appId,
        fields: ['採番'],
        query: query
      }).catch(() => [])
      
      // 取得したレコードの中で最大の連番を特定
      let maxNumber = 0;
      allRecords.forEach(rec => {
        const saibanValue = rec.採番.value;
        if (saibanValue) {
          // 「組織ID-連番」という形式の値から連番部分を抽出
          const parts = saibanValue.split('-');
          if (parts.length >= 2) {
            const numberPart = parts[parts.length - 1];
            const num = parseInt(numberPart, 10);
            if (!isNaN(num) && num > maxNumber) {
              maxNumber = num;
            }
          }
        }
      });
      
      // 最大の連番に1足して、新しい連番を生成(4桁ゼロパディング)
      const newNumber = (maxNumber + 1).toString().padStart(4, '0');
      
      // [採番]に値を設定
      record.採番.value = `${orgId}-${newNumber}`;
      return event;

    // 例外処理
    } catch (error) {
      console.error('採番生成エラー:', error);
      return event;
    }
  });
})();

複数のアプリで共通の連番

アプリ名に「採番」を含む全てのアプリにおいて、7桁の連番で採番する。
前提として kintone REST API Client が必要。

(() => {
  'use strict';

  // 編集可能な全ての画面で[採番]を編集不可にする
  const disableEvents = [
    'app.record.create.show',
    'app.record.edit.show',
    'app.record.index.edit.show'
  ];

  kintone.events.on(disableEvents, (event) => {
    event.record.採番.disabled = true;
    return event;
  });

  // レコード追加画面で保存するときの処理
  kintone.events.on('app.record.create.submit', async (event) => {
    const record = event.record;

    try {
      const client = new KintoneRestAPIClient();
      
      // 全アプリ情報を取得
      const appsResp = await kintone.api(
        kintone.api.url('/k/v1/apps', true),
        'GET',
        {}
      );

      // アプリ名に「採番」が含まれるアプリを絞り込む
      const targetApps = appsResp.apps.filter(app => app.name.includes('採番'));
      
      let maxNumber = 0;

      // 対象アプリから全レコードを取得して[採番]の値の最大値を探す
      for (const app of targetApps) {
        const records = await client.record.getAllRecordsWithCursor({
          app: app.appId,
          fields: ['採番']
        });

        // 新たな最大値が見つかるたび、変数maxNumberに格納
        for (const rec of records) {
          if (rec.採番 && rec.採番.value) {
            const num = parseInt(rec.採番.value, 10);
            if (!isNaN(num) && num > maxNumber) {
              maxNumber = num;
            }
          }
        }
      }

      // maxNumberに1加えて、新しい連番を生成(7桁ゼロパディング)
      const newNumber = (maxNumber + 1).toString().padStart(7, '0');
      
      // [採番]に値を設定
      record.採番.value = newNumber;

      return event;

    } catch (error) {
      console.error('採番処理でエラーが発生しました:', error);
      
      // エラーメッセージを表示して保存を中断
      event.error = '採番に失敗しました。もう一度お試しください。\n' +
                    'エラー詳細: ' + (error.message || JSON.stringify(error));
      return event;
    }
  });
})();

レコード更新のたびに枝番を増やす

連番4桁 + 枝番2桁 の形式で採番。枝番はリビジョンと同値にする。
前提として kintone REST API Client が必要。

(() => {
  'use strict';

  // 編集可能な全ての画面で[採番]を編集不可にする
  const disableEvents = [
    'app.record.create.show',
    'app.record.edit.show',
    'app.record.index.edit.show'
  ];

  kintone.events.on(disableEvents, (event) => {
    event.record.採番.disabled = true;
    return event;
  });

  // レコード保存時の処理
  const submitEvents = [
    'app.record.create.submit',
    'app.record.edit.submit',
    'app.record.index.edit.submit'
  ];

  kintone.events.on(submitEvents, async (event) => {
    const record = event.record;

    try {
      // レコード追加の場合
      if (event.type === 'app.record.create.submit') {
        const client = new KintoneRestAPIClient();
        
        // 現在のアプリIDを取得
        const appId = kintone.app.getId();
      
        // 現在のアプリから全レコードの[採番]の値を取得
        const records = await client.record.getAllRecordsWithCursor({
          app: appId,
          fields: ['採番']
        });

        // [採番]の値から連番部分を抽出して最大値を求める
        let maxNumber = 0;
        for (const rec of records) {
          if (rec.採番.value) {
            // 最初の4桁を取得
            const numberPart = rec.採番.value.substring(0, 4);
            const num = parseInt(numberPart, 10);
            if (!isNaN(num) && num > maxNumber) {
              maxNumber = num;
            }
          }
        }

        // 新しい連番を生成(4桁ゼロパディング)
        const newNumber = (maxNumber + 1).toString().padStart(4, '0');
        
        // レコード追加時のリビジョンは1だから、下2桁の枝番は01
        const branchNumber = '01';
        
        // [採番]に値を設定 (例: 0001-01)
        record.採番.value = newNumber + '-' + branchNumber;
      } 
      // レコード更新の場合
      else {
        // 既存の採番から上4桁の連番を取得(取得した部分は変更せず維持する)
        const currentSaiban = record.採番.value || '';
        const numberPart = currentSaiban.split('-')[0];
        
        // 現在のリビジョン番号を取得(更新後は+1される)
        const currentRevision = event.record.$revision.value;
        const nextRevision = parseInt(currentRevision, 10) + 1;
        
        // 新しい枝番を生成(2桁ゼロパディング)
        const branchNumber = nextRevision.toString().padStart(2, '0');
        
        // [採番]を更新 (例: 0001-02)
        record.採番.value = numberPart + '-' + branchNumber;
      }

      return event;
    } catch (error) {
      console.error('採番エラー:', error);
      event.error = '採番の生成に失敗しました。';
      return event;
    }
  });
})();

プロセス管理のステータスから枝番を生成

連番4桁 + 枝番1桁 の形式で採番。枝番はプロセス管理の設定を取得するAPIの
レスポンスに含まれるステータスの順番と同値にする。

前提として kintone REST API Client が必要。

(() => {
  'use strict';

  // 現在のアプリIDを取得
  const appId = kintone.app.getId();

  // 編集可能な全ての画面で[採番]を編集不可にする
  const disableEvents = [
    'app.record.create.show',
    'app.record.edit.show',
    'app.record.index.edit.show'
  ];

  kintone.events.on(disableEvents, (event) => {
    event.record.採番.disabled = true;
    return event;
  });

  // レコード追加時とプロセス管理のステータス変更時の処理
  const saibanEvents = [
    'app.record.create.submit',
    'app.record.detail.process.proceed'
  ];

  kintone.events.on(saibanEvents, async (event) => {
    const record = event.record;
    const client = new KintoneRestAPIClient();

    try {
      // レコード追加の場合
      if (event.type === 'app.record.create.submit') {
        // 現在のアプリから全レコードの[採番]の値を取得
        const records = await client.record.getAllRecordsWithCursor({
          app: appId,
          fields: ['採番']
        });

        // 既存の[採番]の値から4桁の連番部分の最大値を求める
        let maxNumber = 0;
        for (const rec of records) {
          if (rec.採番.value) {
            const numberPart = rec.採番.value.substring(0, 4);
            const num = parseInt(numberPart, 10);
            if (!isNaN(num) && num > maxNumber) {
              maxNumber = num;
            }
          }
        }

        // 最大値に1を加えて新しい連番を生成し、4桁のゼロパディングを適用
        const newNumber = (maxNumber + 1).toString().padStart(4, '0');
        
        // プロセス管理の初期ステータスの順番を枝番とする
        const branchNumber = '0';
        
        // [採番]に 連番-枝番 の形式で値を設定
        record.採番.value = newNumber + '-' + branchNumber;
      } 
      // プロセス管理のステータス変更の場合
      else if (event.type === 'app.record.detail.process.proceed') {
        // 現在のアプリのプロセス管理設定を取得
        const processSettings = await client.app.getProcessManagement({ app: appId });
        
        // 遷移先のステータス名から、ステータスの順番を取得
        const nextStatus = event.nextStatus.value;
        const statusIndex = processSettings.states[nextStatus].index;
        
        // [採番]の枝番部分を遷移先ステータスの順番に更新
        const currentSaiban = record.採番.value || '';
        const numberPart = currentSaiban.split('-')[0];
        const branchNumber = statusIndex.toString();
        record.採番.value = numberPart + '-' + branchNumber;
      }

      return event;
    } catch (error) {
      console.error('採番エラー:', error);
      event.error = '採番の生成に失敗しました。';
      return event;
    }
  });
})();

一覧画面のボタンを押したときに一括採番

採番 プレフィックス サフィックス というフィールドがあるとする。

一覧画面にある採番ボタンを押すと 採番 の値が無い全レコードを
「プレフィックス-連番_サフィックス」という形式で採番できる。

前提として kintone REST API Client と kuc.min.js が必要。

(() => {
  'use strict';

  const client = new KintoneRestAPIClient();
  
  // 編集可能な画面で[採番]を編集不可にする
  const editEvents = [
    'app.record.create.show',
    'app.record.edit.show',
    'app.record.index.edit.show'
  ];
  
  kintone.events.on(editEvents, (event) => {
    event.record.採番.disabled = true;
    return event;
  });

  // 一覧画面に採番ボタンを設置
  kintone.events.on('app.record.index.show', (event) => {
    if (document.getElementById('numberingButton')) return event;
    const headerSpace = kintone.app.getHeaderMenuSpaceElement();

    const button = new Kuc.Button({
      text: '採番',
      type: 'submit',
      id: 'numberingButton'
    });

    headerSpace.appendChild(button);

    // 採番ボタンクリック時に確認ダイアログを出す
    button.addEventListener('click', async () => {
      const okButton = new Kuc.Button({ text: 'OK', type: 'submit' });
      const cancelButton = new Kuc.Button({ text: 'Cancel', type: 'normal' });
      const confirmFooter = document.createElement('div');
      confirmFooter.style.display = 'flex';
      confirmFooter.style.justifyContent = 'center';
      confirmFooter.style.gap = '8px';
      confirmFooter.appendChild(cancelButton);
      confirmFooter.appendChild(okButton);

      const confirmDialog = new Kuc.Dialog({
        title: '確認',
        content: '未採番のレコードに採番を実行しますか?',
        footer: confirmFooter,
        icon: 'question'
      });

      confirmDialog.open();

      // OKまたはCancelがクリックされるまで処理を待機
      const confirmed = await new Promise((resolve) => {
        // OKがクリックされたら採番開始
        okButton.addEventListener('click', () => { 
          confirmDialog.close(); 
          resolve(true); 
        });
        
        // Cancelがクリックされたら処理中止
        cancelButton.addEventListener('click', () => { 
          confirmDialog.close(); 
          resolve(false); 
        });
      });

      if (!confirmed) return;

      // 採番中はボタンを無効化して連続クリックを防止
      button.disabled = true;
      button.text = '処理中...';

      try {
        const appId = kintone.app.getId();

        // 全レコードを取得
        const allRecords = await client.record.getAllRecordsWithCursor({
          app: appId,
          query: '',
          fields: ['$id', '採番', 'プレフィックス', 'サフィックス']
        });

        // レコードをID昇順にソート
        allRecords.sort((a, b) => a.$id.value - b.$id.value);

        // プレフィックスとサフィックスの組み合わせごとに連番の最大値を特定
        const maxNumberByGroup = {};
        
        allRecords.forEach((record) => {
          const val = record.採番?.value;

          // 採番済のレコードのみ対象にする
          if (val) {
            const match = val.match(/^(.+?)-(\d{5})_(.+)$/);
            
            if (match) {
              const prefix = match[1];
              const num = parseInt(match[2], 10);
              const suffix = match[3];
              const key = `${prefix}|||${suffix}`;

              if (!maxNumberByGroup[key] || num > maxNumberByGroup[key]) {
                maxNumberByGroup[key] = num;
              }
            }
          }
        });

        // 採番したレコード数をカウントする変数
        let updatedCount = 0;
        
        // 更新対象のレコードを格納する配列
        const recordsToUpdate = [];
        
        // 未採番のレコードに採番する
        for (const record of allRecords) {
          if (record.採番?.value) continue;
          const prefix = record.プレフィックス?.value || '';
          const suffix = record.サフィックス?.value || '';
          const key = `${prefix}|||${suffix}`;

          // 上で求めた最大値+1を次の連番とする
          const next = (maxNumberByGroup[key] || 0) + 1;
          maxNumberByGroup[key] = next;

          // 頭を0で埋めて連番を5桁にそろえる
          const padded = String(next).padStart(5, '0');
          
          // 「プレフィックス-連番5桁_サフィックス」の形式で採番
          const numberedValue = `${prefix}-${padded}_${suffix}`;

          // 更新対象配列に追加
          recordsToUpdate.push({
            id: Number(record.$id.value),
            record: { 
              採番: { value: numberedValue }
            }
          });

          // 採番件数をカウントアップ
          updatedCount++;
        }

        // 採番するレコードを一括更新
        const BATCH_SIZE = 100;
        for (let i = 0; i < recordsToUpdate.length; i += BATCH_SIZE) {
          const batch = recordsToUpdate.slice(i, i + BATCH_SIZE);
          await client.record.updateRecords({
            app: appId,
            records: batch
          });
        }

        // 採番完了ダイアログの表示
        const okFinish = new Kuc.Button({ text: 'OK', type: 'submit' });
        const finishFooter = document.createElement('div');
        finishFooter.style.display = 'flex';
        finishFooter.style.justifyContent = 'center';
        finishFooter.style.gap = '8px';
        finishFooter.appendChild(okFinish);

        const finishDialog = new Kuc.Dialog({
          title: '採番完了',
          content: `${updatedCount}件のレコードに採番しました`,
          footer: finishFooter,
          icon: 'success'
        });

        finishDialog.open();

        // OKボタンクリック時の処理
        okFinish.addEventListener('click', () => {
          finishDialog.close();  // ダイアログを閉じる
          
          // 1件でも採番した場合はリロードして採番結果を表示
          if (updatedCount > 0) location.reload();
        });

      // 例外処理
      } catch (error) {
        // コンソールにエラーログ出力
        console.error(`採番処理に失敗しました:${error.message}`);

        // エラーダイアログを作成
        const okErr = new Kuc.Button({ text: 'OK', type: 'submit' });
        const errorFooter = document.createElement('div');
        errorFooter.style.display = 'flex';
        errorFooter.style.justifyContent = 'center';
        errorFooter.style.gap = '8px';
        errorFooter.appendChild(okErr);

        const errorDialog = new Kuc.Dialog({
          title: 'エラー',
          content: `採番処理に失敗しました:${error.message}`,
          footer: errorFooter,
          icon: 'error'
        });

        errorDialog.open();

        // OKボタン押下でダイアログを閉じる
        okErr.addEventListener('click', () => errorDialog.close());
      
      // 採番の成否に関わらず実行する後処理
      } finally {
        button.disabled = false;  // 採番ボタンをクリック可能にする
        button.text = '採番';     // 採番ボタンのテキストを元に戻す
      }

    });

    return event;
  });
})();
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?