
仕訳伝票TSV 生成 (Journal-TSV Create)
仕訳伝票TSVを生成します。請求データ(売上金額・売上日・決済予定日)を元に、売上金の振替伝票TSV(text/tab-separated-values)が生成されます。一括して売上計上するケース、数か月に分けて売上分割するケース、それらが複合するケースに対応します(分割剰余は初月に加算)。 会計システムにインポート可能なファイルを生成させるは、下流工程に「TSV to Excel-CSV FILE」などのコンバータを配置します。
https://support.questetra.com/ja/addons/journal-tsv-create/
2018-12-10 (C) Questetra, Inc. (MIT License)
https://support.questetra.com/ja/addons/journal-tsv-create/
2018-12-10 (C) Questetra, Inc. (MIT License)
Configs
- A: 伝票の摘要名が格納されている文字列型データを選択してください *
- B: 売上高(一括計上分)が格納されている数値型データを選択してください
- C: 売上日(一括計上分)が格納されている日付型データを選択してください
- D: 売上高(分割計上分)が格納されている数値型データを選択してください
- E: 初回の売上日(分割計上分)が格納されている日付型データを選択してください(次月以降は1日計上)
- F: 分割計上の分割数(月数)が格納されている数値型データを選択してください
- G: 決済予定日が格納されている日付型データを選択してください *
- X: TSVデータが格納される文字列型データを選択してください(更新) *
- H: 見出し行をセットしてください(未設定の場合:”取引No,取引日,借方勘定科目,借方補助科目,借方税区分,…” <計21>) #{EL}
- I: 振替伝票IDが挿入される列番号をセットしてください(未設定の場合:”0″ ← ID,[1],[2],…)
- J: 日付が挿入される列番号をセットしてください(未設定の場合:”1″ ← [0],日付,[2],…)
- K: 日付フォーマットをセットしてください(未設定の場合:”YYYY/MM/DD”)
- L: 借方金額が挿入される列番号をセットしてください(未設定の場合:”6″)
- M: 貸方金額が挿入される列番号をセットしてください(未設定の場合:”12″)
- N: 金額を分割計算する際の小数桁数をセットしてください(未設定の場合:”0″)
- O: 伝票の摘要名が挿入される列番号をセットしてください(未設定の場合:”14″)
- P: 売掛金売上行のテンプレートをセットしてください(未設定の場合:”,,売掛金,,対象外,,,,売上高,…”) #{EL}
- Q: 前受金売上行のテンプレートをセットしてください(未設定の場合:”,,前受金,,対象外,,,,売上高,…”) #{EL}
- R: 売掛金回収行のテンプレートをセットしてください(未設定の場合:”,,普通預金,…,売掛金,…未実現,,,”) #{EL}
- S: 前受金回収行のテンプレートをセットしてください(未設定の場合:”,,普通預金,…,前受金,…未実現,,,”) #{EL}
Script
// Generator from Invoice Data to Journal Slip TSV
// (c) 2018, Questetra, Inc. (the MIT License)
// ◇Money Forward (journal_sample.csv)
// https://support.biz.moneyforward.com/account/guide/import-books/ib01.html
// 取引No,取引日,借方勘定科目,借方補助科目,借方税区分,借方部門,借方金額(円),借方税額,貸方勘定科目,貸方補助科目,貸方税区分,貸方部門,貸方金額(円),貸方税額,摘要,仕訳メモ,タグ,MF仕訳タイプ,決算整理仕訳,×作成日時,×最終更新日時
// ※「取引No」「取引日」「勘定科目」「金額」については入力必須項目
// ◇Freee (仕訳インポートその他(借方金額・貸方金額あり)_for_excel.csv)
// https://support.freee.co.jp/hc/ja/articles/204847430
// 日付,伝票番号,決算整理仕訳,借方勘定科目,借方科目コード,借方補助科目,借方補助科目コード,借方部門,借方金額,借方内税/外税,借方税区分,借方税額,貸方勘定科目,貸方科目コード,貸方補助科目,貸方補助科目コード,貸方部門,貸方金額,貸方内税/外税,貸方税区分,貸方税額,摘要
// ※「日付」「勘定科目」「金額」必須
main();
function main(){ //////// main() start ////////
//// == Config Retrieving / 工程コンフィグの参照 ==
const dataIdA = configs.get( "conf_DataIdA" ) + ""; // required
const dataIdB = configs.get( "conf_DataIdB" ) + "";
const dataIdC = configs.get( "conf_DataIdC" ) + "";
const dataIdD = configs.get( "conf_DataIdD" ) + "";
const dataIdE = configs.get( "conf_DataIdE" ) + "";
const dataIdF = configs.get( "conf_DataIdF" ) + "";
const dataIdG = configs.get( "conf_DataIdG" ) + ""; // required
const dataIdX = configs.get( "conf_DataIdX" ) + ""; // required
let headerRow = configs.get( "conf_HeaderRow" ) + "";
if( headerRow === ""){headerRow = "取引No,取引日,借方勘定科目,借方補助科目,借方税区分,借方部門,借方金額(円),借方税額,貸方勘定科目,貸方補助科目,貸方税区分,貸方部門,貸方金額(円),貸方税額,摘要,仕訳メモ,タグ,MF仕訳タイプ,決算整理仕訳,作成日時,最終更新日時";}
let colSlipId = configs.get( "conf_ColSlipId" ) + "";
if( colSlipId === ""){colSlipId = 0;}else{colSlipId = Number(colSlipId);}
let colDate = configs.get( "conf_ColDate" ) + "";
if( colDate === ""){colDate = 1;}else{colDate = Number(colDate);}
let ymdFormat = configs.get( "conf_YmdFormat" ) + "";
if( ymdFormat === ""){ymdFormat = "YYYY/MM/DD";}
let colDrAmt = configs.get( "conf_ColDrAmt" ) + "";
if( colDrAmt === ""){colDrAmt = 6 ;}else{colDrAmt = Number(colDrAmt);}
let colCrAmt = configs.get( "conf_ColCrAmt" ) + "";
if( colCrAmt === ""){colCrAmt = 12;}else{colCrAmt = Number(colCrAmt);}
const decimalPlaces = configs.get( "conf_DecimalPlaces" ) - 0;
let colSlipDescr = configs.get( "conf_ColSlipDescr" ) + "";
if( colSlipDescr === ""){colSlipDescr = 14;}else{colSlipDescr = Number(colSlipDescr);}
let tplReceivableSales = configs.get( "conf_TplReceivableSales" ) + "";
if( tplReceivableSales === ""){tplReceivableSales = ",,売掛金,,対象外,,,0,売上高,,課税売上 8%,,,0,,,,,,,";}
const arrTplReceivableSales = tplReceivableSales.split(",");
let tplReceivedSales = configs.get( "conf_TplReceivedSales" ) + "";
if( tplReceivedSales === ""){tplReceivedSales = ",,前受金,,対象外,,,0,売上高,,課税売上 8%,,,0,,,,,,,";}
const arrTplReceivedSales = tplReceivedSales.split(",");
let tplPaymentReceivable = configs.get( "conf_TplPaymentReceivable" ) + "";
if( tplPaymentReceivable === ""){tplPaymentReceivable = ",,普通預金,,対象外,,,0,売掛金,,対象外,,,0,,,,,,,";}
const arrTplPaymentReceivable = tplPaymentReceivable.split(",");
let tplPaymentReceived = configs.get( "conf_TplPaymentReceived" ) + "";
if( tplPaymentReceived === ""){tplPaymentReceived = ",,普通預金,,対象外,,,0,前受金,,対象外,,,0,,,,,,,";}
const arrTplPaymentReceived = tplPaymentReceived.split(",");
// 'java.lang.String' to javascript primitive 'string' or 'number'
//// == Data Retrieving / ワークフローデータの参照 ==
const slipDescr = engine.findDataByNumber( dataIdA ) + ""; // required
let salesAmtSingle = engine.findDataByNumber( dataIdB ) - 0; // null-0 -> 0
let salesAmtSingleDate = engine.findDataByNumber( dataIdC ) + "";
let salesAmtMultiple = engine.findDataByNumber( dataIdD ) - 0;
let salesAmtMultipleDate = engine.findDataByNumber( dataIdE ) + "";
let salesAmtMultipleNum = engine.findDataByNumber( dataIdF ) - 0;
const dueDate = engine.findDataByNumber( dataIdG ) + ""; // required
// 'java.lang.String' to javascript primitive 'string' or 'number'
const dateDueDate = new Date( dueDate );
const myNumOfCols = headerRow.split(",").length;
/// Input Errors
// Number of Columns
if( myNumOfCols !== tplReceivableSales.split(",").length ){
engine.log( "The number of commas in each Config is not the same. (tplReceivableSales)" );
return;
}else if( myNumOfCols !== tplReceivedSales.split(",").length ){
engine.log( "The number of commas in each Config is not the same. (tplReceivedSales)" );
return;
}else if( myNumOfCols !== tplPaymentReceivable.split(",").length ){
engine.log( "The number of commas in each Config is not the same. (tplPaymentReceivable)" );
return;
}else if( myNumOfCols !== tplPaymentReceived.split(",").length ){
engine.log( "The number of commas in each Config is not the same. (tplPaymentReceived)" );
return;
}
// Zero Amount
if(salesAmtSingle === 0 && salesAmtMultiple === 0){ //Case 0: Error exit
engine.log( "Total Sales Amount is zero." );
return;
}
//// == Calculating / 演算 ==
const dateToday = new Date();
const todaySerial = (dateToday.getFullYear() % 100) * 10000 + (dateToday.getMonth()+1) * 100 + dateToday.getDate();
// e.g. 180131
let tmpSlipId = (todaySerial % 10000) * 1000000 + (processInstance.getProcessInstanceId() % 10000) * 100;
// e.g. (0)131 123400 (3 or 4digits, 4 digits, 2 digits: for Unsigned Int limit of Money Forward)
/// If the number of assumed slips per day does not exceed 100 , / 想定伝票数が日次100枚以下の場合の改変例
// let tmpSlipId = todaySerial * 10000 + (processInstance.getProcessInstanceSequenceNumber() % 100) * 100;
// e.g. 1801319900 (6 digits, 2 digits, 2 digits)
/// If the number of assumed slips per day exceeds 10000, / 想定伝票数が日次10000枚を超える場合の改変例
// e.g. 1234567 00 (max 7 digits, 2 digits) // No Date Info 日付情報を削除
// let tmpSlipId = (processInstance.getProcessInstanceId() % 10000000) * 100;
let myTsv = "";
myTsv += headerRow.replace(/,/g, "\t") + "\n";
//Case 1: Only Single Sales / 一括売上だけの場合
if(salesAmtSingle !== 0 && salesAmtMultiple === 0){
let dateSalesAmtSingleDate = new Date( salesAmtSingleDate );
if( dateSalesAmtSingleDate.getTime() <= dateDueDate.getTime() ){
tmpSlipId++;
let tmpArrSales = arrTplReceivableSales.concat();
tmpArrSales[colSlipId] = tmpSlipId + "";
tmpArrSales[colDate] = funcDateStr( dateSalesAmtSingleDate, ymdFormat );
tmpArrSales[colDrAmt] = salesAmtSingle;
tmpArrSales[colCrAmt] = salesAmtSingle;
tmpArrSales[colSlipDescr] = slipDescr;
myTsv += tmpArrSales.join("\t");
myTsv += "\n";
tmpSlipId++;
let tmpArrPay = arrTplPaymentReceivable.concat();
tmpArrPay[colSlipId] = tmpSlipId + "";
tmpArrPay[colDate] = funcDateStr( dateDueDate, ymdFormat );
tmpArrPay[colDrAmt] = salesAmtSingle;
tmpArrPay[colCrAmt] = salesAmtSingle;
tmpArrPay[colSlipDescr] = slipDescr;
myTsv += tmpArrPay.join("\t");
}else{
tmpSlipId++;
let tmpArrSales = arrTplReceivedSales.concat();
tmpArrSales[colSlipId] = tmpSlipId + "";
tmpArrSales[colDate] = funcDateStr( dateSalesAmtSingleDate, ymdFormat );
tmpArrSales[colDrAmt] = salesAmtSingle;
tmpArrSales[colCrAmt] = salesAmtSingle;
tmpArrSales[colSlipDescr] = slipDescr;
myTsv += tmpArrSales.join("\t");
myTsv += "\n";
tmpSlipId++;
let tmpArrPay = arrTplPaymentReceived.concat();
tmpArrPay[colSlipId] = tmpSlipId + "";
tmpArrPay[colDate] = funcDateStr( dateDueDate, ymdFormat );
tmpArrPay[colDrAmt] = salesAmtSingle;
tmpArrPay[colCrAmt] = salesAmtSingle;
tmpArrPay[colSlipDescr] = slipDescr;
myTsv += tmpArrPay.join("\t");
}
}
//Case 2: Including Multiple Sales / 分割売上を含む場合
if(salesAmtMultiple !== 0){
if(salesAmtMultipleNum === 0){
engine.log( "'0' can not be used for division number." );
return;
}
// Subtotal of ReceivableSales and ReceivedSales
let myReceivableSales = 0;
let myReceivedSales = 0;
//// Single
if(salesAmtSingle !== 0){
let dateSalesAmtSingleDate = new Date( salesAmtSingleDate );
if( dateSalesAmtSingleDate.getTime() <= dateDueDate.getTime() ){
tmpSlipId++;
let tmpArrSales = arrTplReceivableSales.concat();
tmpArrSales[colSlipId] = tmpSlipId + "";
tmpArrSales[colDate] = funcDateStr( dateSalesAmtSingleDate, ymdFormat );
tmpArrSales[colDrAmt] = salesAmtSingle;
tmpArrSales[colCrAmt] = salesAmtSingle;
tmpArrSales[colSlipDescr] = slipDescr;
myTsv += tmpArrSales.join("\t");
myTsv += "\n";
myReceivableSales += salesAmtSingle;
}else{
tmpSlipId++;
let tmpArrSales = arrTplReceivedSales.concat();
tmpArrSales[colSlipId] = tmpSlipId + "";
tmpArrSales[colDate] = funcDateStr( dateSalesAmtSingleDate, ymdFormat );
tmpArrSales[colDrAmt] = salesAmtSingle;
tmpArrSales[colCrAmt] = salesAmtSingle;
tmpArrSales[colSlipDescr] = slipDescr;
myTsv += tmpArrSales.join("\t");
myTsv += "\n";
myReceivedSales += salesAmtSingle;
}
}
//// Multiple
// Sales for each month
// The remainder is added to the first month. / 商の剰余は初月に加算
// "101/10" is divided into "10.10" * 10, in the case of two decimal places.
// "101/10" is divided into "11" * 1 and "10" * 9, in the case of 0 decimal place.
const monthlySalesFollow = Math.floor( salesAmtMultiple / salesAmtMultipleNum * Math.pow(10, decimalPlaces) ) / Math.pow(10, decimalPlaces);
const monthlySalesFirst = salesAmtMultiple - monthlySalesFollow * ( salesAmtMultipleNum - 1 );
let arrSalesAmtMultipleDate = new Array( salesAmtMultipleNum );
// First Month
arrSalesAmtMultipleDate[0] = new Date( salesAmtMultipleDate );
if( arrSalesAmtMultipleDate[0].getTime() <= dateDueDate.getTime() ){
tmpSlipId++;
let tmpArrSales = arrTplReceivableSales.concat();
tmpArrSales[colSlipId] = tmpSlipId + "";
tmpArrSales[colDate] = funcDateStr( arrSalesAmtMultipleDate[0], ymdFormat );
tmpArrSales[colDrAmt] = monthlySalesFirst;
tmpArrSales[colCrAmt] = monthlySalesFirst;
tmpArrSales[colSlipDescr] = funcMonthStr( arrSalesAmtMultipleDate[0] ) + " @ " + slipDescr;
myTsv += tmpArrSales.join("\t");
myTsv += "\n";
myReceivableSales += monthlySalesFirst;
}else{
tmpSlipId++;
let tmpArrSales = arrTplReceivedSales.concat();
tmpArrSales[colSlipId] = tmpSlipId + "";
tmpArrSales[colDate] = funcDateStr( arrSalesAmtMultipleDate[0], ymdFormat );
tmpArrSales[colDrAmt] = monthlySalesFirst;
tmpArrSales[colCrAmt] = monthlySalesFirst;
tmpArrSales[colSlipDescr] = funcMonthStr( arrSalesAmtMultipleDate[0] ) + " @ " + slipDescr;
myTsv += tmpArrSales.join("\t");
myTsv += "\n";
myReceivedSales += monthlySalesFirst;
}
// Following Months
// Record on 1st of each month / 次月以降は1日に売上計上
for( let i=1; i < salesAmtMultipleNum; i++){
arrSalesAmtMultipleDate[i] = new Date(
arrSalesAmtMultipleDate[0].getFullYear(),
arrSalesAmtMultipleDate[0].getMonth() + i,
1); // 1st of each month
if( arrSalesAmtMultipleDate[i].getTime() <= dateDueDate.getTime() ){
tmpSlipId++;
let tmpArrSales = arrTplReceivableSales.concat();
tmpArrSales[colSlipId] = tmpSlipId + "";
tmpArrSales[colDate] = funcDateStr( arrSalesAmtMultipleDate[i], ymdFormat );
tmpArrSales[colDrAmt] = monthlySalesFollow;
tmpArrSales[colCrAmt] = monthlySalesFollow;
tmpArrSales[colSlipDescr] = funcMonthStr( arrSalesAmtMultipleDate[i] ) + " @ " + slipDescr;
myTsv += tmpArrSales.join("\t");
myTsv += "\n";
myReceivableSales += monthlySalesFollow;
}else{
tmpSlipId++;
let tmpArrSales = arrTplReceivedSales.concat();
tmpArrSales[colSlipId] = tmpSlipId + "";
tmpArrSales[colDate] = funcDateStr( arrSalesAmtMultipleDate[i], ymdFormat );
tmpArrSales[colDrAmt] = monthlySalesFollow;
tmpArrSales[colCrAmt] = monthlySalesFollow;
tmpArrSales[colSlipDescr] = funcMonthStr( arrSalesAmtMultipleDate[i] ) + " @ " + slipDescr;
myTsv += tmpArrSales.join("\t");
myTsv += "\n";
myReceivedSales += monthlySalesFollow;
}
}
if(myReceivableSales !== 0 && myReceivedSales !== 0){
tmpSlipId++;
let tmpArrPay1 = arrTplPaymentReceivable.concat();
tmpArrPay1[colSlipId] = tmpSlipId + "";
tmpArrPay1[colDate] = funcDateStr( dateDueDate, ymdFormat );
tmpArrPay1[colDrAmt] = myReceivableSales + myReceivedSales;
tmpArrPay1[colCrAmt] = myReceivableSales;
tmpArrPay1[colSlipDescr] = slipDescr;
myTsv += tmpArrPay1.join("\t");
myTsv += "\n";
let tmpArrPay2 = arrTplPaymentReceived.concat();
tmpArrPay2[colSlipId] = tmpSlipId + "";
tmpArrPay2[colDate] = funcDateStr( dateDueDate, ymdFormat );
tmpArrPay2[colDrAmt] = 0;
tmpArrPay2[colCrAmt] = myReceivedSales;
tmpArrPay2[colSlipDescr] = slipDescr;
myTsv += tmpArrPay2.join("\t");
}else if(myReceivableSales === 0){
tmpSlipId++;
let tmpArrPay = arrTplPaymentReceived.concat();
tmpArrPay[colSlipId] = tmpSlipId + "";
tmpArrPay[colDate] = funcDateStr( dateDueDate, ymdFormat );
tmpArrPay[colDrAmt] = myReceivableSales + myReceivedSales;
tmpArrPay[colCrAmt] = myReceivedSales;
tmpArrPay[colSlipDescr] = slipDescr;
myTsv += tmpArrPay.join("\t");
}else if(myReceivedSales === 0){
tmpSlipId++;
let tmpArrPay = arrTplPaymentReceivable.concat();
tmpArrPay[colSlipId] = tmpSlipId + "";
tmpArrPay[colDate] = funcDateStr( dateDueDate, ymdFormat );
tmpArrPay[colDrAmt] = myReceivableSales + myReceivedSales;
tmpArrPay[colCrAmt] = myReceivableSales;
tmpArrPay[colSlipDescr] = slipDescr;
myTsv += tmpArrPay.join("\t");
}
}
//// == Data Updating / ワークフローデータへの代入 ==
engine.setDataByNumber( dataIdX, myTsv );
} //////// main() end ////////
function funcDateStr( dateObj, formatStr ){
formatStr = formatStr.replace(/YYYY/g, dateObj.getFullYear());
formatStr = formatStr.replace(/MM/g, ('00' + (dateObj.getMonth() + 1)).slice(-2));
formatStr = formatStr.replace(/DD/g, ('00' + dateObj.getDate()).slice(-2));
return formatStr;
}
function funcMonthStr( dateObj ){
let myStr = dateObj.getFullYear() + "-" + ("00" + (dateObj.getMonth()+1)).slice(-2);
return myStr;
}
Download
Capture

Notes
- TSV データの各行(各レコード)が振替伝票(仕訳伝票)1枚に相当します
- TSV データのままでは会計システム(会計ソフト/会計クラウド)にインポートできません。「インポートファイル」は、下流工程に「TSV to Excel-CSV FILE」などの自動変換工程(コンバータ)を配置して生成させます
- 売上の振替伝票は、売上計上日が「決済予定日」と同日もしくは前の場合は「売掛金売上行のテンプレート」が適用され、売上計上日が「決済予定日」より後の場合は「前受金売上行のテンプレート」が適用されます
- 現預金回収の振替伝票(入金仕訳)は「決済予定日」の日付にて生成されます(複合仕訳)。(実際の入金日情報は会計システム側で修正する必要があります)
- インポートファイルの「見出し行」は会計システムによって異なります。コンフィグ項目「H」にてカンマ区切りで設定してください。無指定の場合は「MFクラウド会計」の「見出し行」(21項目)が自動的にセットされます
- MFクラウド会計の見出し行(21項目): 取引No,取引日,借方勘定科目,借方補助科目,借方税区分,借方部門,借方金額(円),借方税額,貸方勘定科目,貸方補助科目,貸方税区分,貸方部門,貸方金額(円),貸方税額,摘要,仕訳メモ,タグ,MF仕訳タイプ,決算整理仕訳,作成日時,最終更新日時
- Freeeの見出し行(22項目): 日付,伝票番号,決算整理仕訳,借方勘定科目,借方科目コード,借方補助科目,借方補助科目コード,借方部門,借方金額,借方内税/外税,借方税区分,借方税額,貸方勘定科目,貸方科目コード,貸方補助科目,貸方補助科目コード,貸方部門,貸方金額,貸方内税/外税,貸方税区分,貸方税額,摘要
- 各列におけるデータ挿入位置やフォーマットは会計システムによって異なります。コンフィグ項目「I~O」にて設定してください。無指定の場合は「MFクラウド会計」の仕様が自動的にセットされます
- 各行のテンプレートは、売掛金売上・前受金売上・売掛金回収・前受金回収の4パターンについて、コンフィグ項目「P~S」にて設定してください。無指定の場合は「MFクラウド会計」の仕様が自動的にセットされます
- もしテンプレートにおいてワークフローデータを参照したい場合には、EL式にて設定します。たとえば勘定科目「売掛金」の補助科目の位置に『#{data[’14’]}』と設定すれば案件データが自動参照され「〇〇株式会社」といった補助科目名が記録されます。あるいはまた勘定科目「売上高」の補助科目の位置に『#{data[’13’]}』と設定すれば案件データが自動参照され「SaaS」といった補助科目名が記録されます
- ,,売掛金,#{data[’14’]},対象外,,,0,売上高,#{data[’13’]},課税売上 8%,,,0,,,,,,,
- JavaScript知識のある開発者の方であれば「取引No」(振替伝票ID)のフォーマットをカスタマイズすることも可能です。定義ファイルの変数「tmpSlipId」の代入式を編集してください
- 会計システムによる桁数制限に注意してください。たとえば「MFクラウド会計」の場合、「Unsigned Int の最大値」(4,294,967,295)を超えるとエラーになります
// もし3月決算の会社なら、、、
// “1月 2月 3月” を “13月 14月 15月” にすると便利さUP!(let myTsv の前行に追記)
if( tmpSlipId < 400000000 ){ tmpSlipId += 1200000000; }