今回は個人事業主の方や小規模で会社を経営されている方に向けた事業用必要書類の自動作成システムを作成しましたので、使い方と合わせて紹介します。
「見積書」「請求書」「領収書」「納品書」「業務契約書」「発注書」をPDFで出力しGoogleDrive上に自動管理してくれるシステムになっています。
案件ごとに各書類を自動で分類して整理してくれる機能があるため、このシステムを用いて業務管理をすれば効率化間違いなしです!
下記ボタンをクリックしてスプレッドシートをご自身のGoogleDriveにコピーしてください。
リンクを開いて左上の「ファイル」→「コピーを作成」をクリックすることで自分自身のGoogleアカウントに紐づいたGoogleDriveにスプレッドシートをコピーすることができます。


このシステムの全体像について解説します。
このシステムはスプレッドシートの「案件管理」シートをベースにしてGASを用いて各書類をPDF化しGoogleDriveに保存する動きになっています。
案件管理シートの「管理番号」を元にGoogleDriveのフォルダや各書類の書類番号を整理することで案件管理シートを参照すれば、どの案件がどの書類に紐づいているかがすぐわかるようになっています。
各シートのシート名はGASで定数で指定されています。
GASについて詳しくない方については変更はお勧めできません。はじめにGASを学んでから修正をするようにしてください。
システムを動作させるにあたってGASの内容を修正する必要がありますので、それを実施していきます。
「拡張機能」→「Apps Script」の順にクリックしてスクリプトエディタを開いてください。
下記の「SS_ID」にはコピーしたスプレッドシートのIDを入力し、「LogsFolder_ID」にはPDFが保存されるGoogleDriveのフォルダーのIDを入力してください。
スプレッドシートのIDはURLを「docs.google.com/spreadsheets/d/○○/edit」としたときの「○○」の部分になります。
GoogleDriveのフォルダーのIDはフォルダーを開い時のURLを「drive.google.com/drive/folders/〇〇」としたときの「○○」の部分になります。
const SS_ID = ''
const LogsFolder_ID = ''
準備ができたらスクリプトを保存して、スクリプトエディタを閉じてください。

基本的な入力のスタートは E列の「案件名(空白なし)」とD列の「登録年月日」を入力することから始まります。
案件名を入力すると A列の「管理番号」に連番が自動で割り付けられるようになっています。これは関数を用いているのでA列の関数を編集することは基本的にしないようにしてください。
F列の「金額」については案件ごとの売上管理をしやすくするためのメモになります。任意のものに変更してもシステム上は問題ありません。同様にC列の「完了状態」についても案件の状況をメモするものになりますので、システム上は変更されても問題ありません。
続いて、H列からL列の各書類の書類番号ですが、ここにユーザーがデータを入力することはなく、システムが参照したりデータの紐付けに使用します。後に説明するPDF自動生成を行うと管理番号(案件)に紐づいた書類の書類番号を生成してくれるので管理が非常にしやすくなります。
G列の「発行書類」についてはプルダウンの中から「見積書」「請求書」「領収書」「納品書」「業務契約書」「発注書」が複数選択し、案件に対して発行したい書類を選択します。
最後にB列の「発行状態」についてです。こちらについては「処理対象」「処理済」が選択できますが、先ほど説明した「発行書類」をPDF化する際にどの案件が発行対象なのかを指定するものになります。「処理対象」にすることで該当案件をシステムが検知してPDF化することができるようになります。処理対象は1つでなければシステムでエラーが発生します。
最後にA列とB列の間にある「作成」ボタンになります。上記までの操作を実施した状態でこちらのボタンをクリックするとGASが動作し各書類を作成すると同時に案件管理番号ごとにGoogleDriveを作成してそこに保存してくれます。
ただし、PDF化される書類は後に説明する各書類シートを参照するため、事前に金額等を入力しておいてください。

上の画像で赤く囲った部分を基本的に編集します。
それ以外については関数で自動計算を行なっています。必要に応じて形式を変えるなどをしてください。
なお、GASでは「見積書番号」のセルのみを参照しているので、その部分を編集するとシステムが動作しなくなる可能性があります。

上の画像で赤く囲った部分を基本的に編集します。
それ以外については関数で自動計算を行なっています。必要に応じて形式を変えるなどをしてください。
なお、GASでは「請求書番号」のセルのみを参照しているので、その部分を編集するとシステムが動作しなくなる可能性があります。

上の画像で赤く囲った部分を基本的に編集します。
それ以外については関数で自動計算を行なっています。必要に応じて形式を変えるなどをしてください。
なお、GASでは「領収書番号」のセルのみを参照しているので、その部分を編集するとシステムが動作しなくなる可能性があります。

上の画像で赤く囲った部分を基本的に編集します。
それ以外については関数で自動計算を行なっています。必要に応じて形式を変えるなどをしてください。
なお、GASでは「領収書番号」のセルのみを参照しているので、その部分を編集するとシステムが動作しなくなる可能性があります。

上の画像で赤く囲った部分を基本的に編集します。
それ以外については関数で自動計算を行なっています。必要に応じて形式を変えるなどをしてください。
なお、GASでは「契約書番号」のセルのみを参照しているので、その部分を編集するとシステムが動作しなくなる可能性があります。

上の画像で赤く囲った部分を基本的に編集します。
それ以外については関数で自動計算を行なっています。必要に応じて形式を変えるなどをしてください。
なお、GASでは「発注書番号」のセルのみを参照しているので、その部分を編集するとシステムが動作しなくなる可能性があります。
下記はコピーされたスプレッドシートに連携されているGASのコード全容です。
「PrintPapers」は「案件管理」シートの「作成」ボタンをクリックしたときに実行される関数です。
入力されているデータを確認して間違いがないかを確認してもしエラーなどがあれば、メッセージボックスでエラー内容を表示するようになっています。
これらに問題がなければGoogleDriveにフォルダを作りながらPDFを生成します。
「SaveSheetAsPdf」は各書類のシートをPDF化する関数です。
シート名を参照して適したサイズにしてPDF化する処理を行なっています。
コードについて質問があればコメントにお寄せください。
function PrintPapers() {
const SS_ID = ''
const LogsFolder_ID = ''
let SS = SpreadsheetApp.openById(SS_ID)
let QuotationSH = SS.getSheetByName('【受託】見積書')
let InvoiceSH = SS.getSheetByName('【受託】請求書')
let ReceiptSH = SS.getSheetByName('【受託】領収書')
let DeliverySH = SS.getSheetByName('【受託】納品書')
let AgreementSH = SS.getSheetByName('【受託・委託】業務契約書')
let ManageSH = SS.getSheetByName('案件管理')
let Data = ManageSH.getDataRange().getValues()
var FilteredData = Data.filter(function(row) {
return row[0] !== ''
})
let Process_Cnt = FilteredData.filter(function(row) {
return row[1] === '処理対象';
}).length
if(Process_Cnt != 1){
Browser.msgBox('書類発行案件数エラー:'+Process_Cnt+'個の案件が選択されています。')
return
}
for(let Idx=FilteredData.length-1; Idx > 0;Idx--){
if(FilteredData[Idx][1] != '処理対象') continue
let ProcessData = FilteredData[Idx]
let TargetPaperList = ProcessData[6].split(", ")
if(TargetPaperList.length === 0){
Browser.msgBox('発行書類数エラー:発行書類が一つも選択されていません。管理番号:'+ProcessData[0])
return
}
// 案件(管理番号)ごとのフォルダを探索or作成する
let LogsFolder = DriveApp.getFolderById(LogsFolder_ID)
let ProjectFolders = LogsFolder.getFolders()
let ProjectFolderId = null
while (ProjectFolders.hasNext()) {
let ProjectFolder = ProjectFolders.next()
if (ProjectFolder.getName() === ProcessData[0]) {
ProjectFolderId = ProjectFolder.getId()
break;
}
}
if (ProjectFolderId === null) {
let NewFolder = LogsFolder.createFolder(ProcessData[0])
ProjectFolderId = NewFolder.getId()
}
for(let PaperIdx=0; PaperIdx < TargetPaperList.length; PaperIdx++){
// 処理対象の書類の文字列を取得
let TargetPaper = TargetPaperList[PaperIdx]
// 書類ごとのフォルダを探索or作成する
let ProjectFolder = DriveApp.getFolderById(ProjectFolderId)
let PapersFolders = ProjectFolder.getFolders()
let PaperFolderId = null
while (PapersFolders.hasNext()) {
let PaperFolder = PapersFolders.next()
if (PaperFolder.getName() === TargetPaper) {
PaperFolderId = PaperFolder.getId()
break;
}
}
if (PaperFolderId === null) {
let NewFolder = ProjectFolder.createFolder(TargetPaper)
PaperFolderId = NewFolder.getId()
}
if(TargetPaper == '見積書'){
// 書類番号を作成
let PaperNumber = ''
if(ProcessData[7] === ''){
PaperNumber = 'MIT-'+ProcessData[0]+'-001'
}else{
let PaperCounterList = ProcessData[7].split('-')
PaperNumber = 'MIT-'+ProcessData[0]+'-'+Utilities.formatString('%03d', 1*(PaperCounterList[PaperCounterList.length-1])+1)
}
let PaperNumberRange = QuotationSH.getRange('S5:Y5')
PaperNumberRange.setValue(PaperNumber)
// PDFを作成
let FileName = PaperNumber+'_'+ProcessData[4]+'_見積書'
SaveSheetAsPdf(SS_ID,'【受託】見積書',PaperFolderId,FileName)
ProcessData[7] = PaperNumber
}else if(TargetPaper == '請求書'){
// 書類番号を作成
let PaperNumber = ''
if(ProcessData[8] === ''){
PaperNumber = 'SEI-'+ProcessData[0]+'-001'
}else{
let PaperCounterList = ProcessData[8].split('-')
PaperNumber = 'SEI-'+ProcessData[0]+'-'+Utilities.formatString('%03d', 1*(PaperCounterList[PaperCounterList.length-1])+1)
}
let PaperNumberRange = InvoiceSH.getRange('S5:Y5')
PaperNumberRange.setValue(PaperNumber)
// PDFを作成
let FileName = PaperNumber+'_'+ProcessData[4]+'_請求書'
SaveSheetAsPdf(SS_ID,'【受託】請求書',PaperFolderId,FileName)
ProcessData[8] = PaperNumber
}else if(TargetPaper == '領収書'){
// 書類番号を作成
let PaperNumber = ''
if(ProcessData[9] === ''){
PaperNumber = 'RYO-'+ProcessData[0]+'-001'
}else{
let PaperCounterList = ProcessData[9].split('-')
PaperNumber = 'RYO-'+ProcessData[0]+'-'+Utilities.formatString('%03d', 1*(PaperCounterList[PaperCounterList.length-1])+1)
}
let PaperNumberRange = ReceiptSH.getRange('S5:Y5')
PaperNumberRange.setValue(PaperNumber)
// PDFを作成
let FileName = PaperNumber+'_'+ProcessData[4]+'_領収書'
SaveSheetAsPdf(SS_ID,'【受託】領収書',PaperFolderId,FileName)
ProcessData[9] = PaperNumber
}else if(TargetPaper == '納品書'){
// 書類番号を作成
let PaperNumber = ''
if(ProcessData[10] === ''){
PaperNumber = 'NOU-'+ProcessData[0]+'-001'
}else{
let PaperCounterList = ProcessData[10].split('-')
PaperNumber = 'NOU-'+ProcessData[0]+'-'+Utilities.formatString('%03d', 1*(PaperCounterList[PaperCounterList.length-1])+1)
}
let PaperNumberRange = DeliverySH.getRange('S5:Y5')
PaperNumberRange.setValue(PaperNumber)
// PDFを作成
let FileName = PaperNumber+'_'+ProcessData[4]+'_納品書'
SaveSheetAsPdf(SS_ID,'【受託】納品書',PaperFolderId,FileName)
ProcessData[10] = PaperNumber
}else if(TargetPaper == '契約書'){
// 書類番号を作成
let PaperNumber = ''
if(ProcessData[11] === ''){
PaperNumber = 'KEI-'+ProcessData[0]+'-001'
}else{
let PaperCounterList = ProcessData[11].split('-')
PaperNumber = 'KEI-'+ProcessData[0]+'-'+Utilities.formatString('%03d', 1*(PaperCounterList[PaperCounterList.length-1])+1)
}
let PaperNumberRange = AgreementSH.getRange('S5:Y5')
PaperNumberRange.setValue(PaperNumber)
// PDFを作成
let FileName = PaperNumber+'_'+ProcessData[4]+'_契約書'
SaveSheetAsPdf(SS_ID,'【受託・委託】業務契約書',PaperFolderId,FileName)
ProcessData[11] = PaperNumber
}
}
// 管理シートの状態を更新
ProcessData[1] = '処理済'
ManageSH.getRange('A'+(Idx+1)+':M'+(Idx+1)).setValues([ProcessData])
}
}
function SaveSheetAsPdf(SpreadsheetId, SheetName, FolderId, FileName) {
// スプレッドシートの更新
SpreadsheetApp.flush();
let SS = SpreadsheetApp.openById(SpreadsheetId)
let Sheet = SS.getSheetByName(SheetName)
let Folder = DriveApp.getFolderById(FolderId)
let ExportUrl = "https://docs.google.com/spreadsheets/d/" + SpreadsheetId + "/export?format=pdf" +
"&gid=" + Sheet.getSheetId() +
"&size=A4" +
"&portrait=true" +
"&fitw=true" +
"&top_margin=0.50" +
"&bottom_margin=0.50" +
"&left_margin=0.50" +
"&right_margin=0.50" +
"&sheetnames=false" +
"&printtitle=false" +
"&pagenumbers=false" +
"&gridlines=false" +
"&fzr=false"
let Token = ScriptApp.getOAuthToken()
let Response = UrlFetchApp.fetch(ExportUrl, {
headers: {
"Authorization": "Bearer " + Token
}
})
let Blob = Response.getBlob()
let FinalFileName = FileName ? FileName : SheetName // ファイル名が指定されていれば使う、なければシート名を使う
Blob.setName(FinalFileName + ".pdf")
let File = Folder.createFile(Blob)
}
システムによってPDFを作成した後は書類番号が「案件管理」シートに転記されるのでそれをGoogleDriveで検索することで該当ファイルを得ることができます。
これで案件ごとに発行した書類の管理と案件の紐付けを簡単に行うことができます。
見積書などの再発行に関しては、再度「作成」ボタンを押すことで書類番号の末尾の数値が増加し、重複を防いでくれます。
今回は個人事業主の方や小規模の会社を運営されている方々には必見となる業務上の必要書類を自動で作成、GoogleDriveで保存管理するシステムの紹介をしてきました。
GASを学ぶことで無駄なコストをかけずに簡単な業務効率化を行うことができます。
もし、今回のシステムがいいなと思ったらお気に入り登録と他の記事を見ていってもらえたらと思います!!
また、ご質問等があればコメントまでよろしくお願いします!!