시간 추적 및 청구서 자동화
중급
이것은Document Extraction분야의자동화 워크플로우로, 15개의 노드를 포함합니다.주로 Code, Jira, Gmail, Merge, ManualTrigger 등의 노드를 사용하며. Jira와 Gmail로 개발자 인voice 및 규정 준수 알림 자동 생성
사전 요구사항
- •Google 계정 및 Gmail API 인증 정보
사용된 노드 (15)
카테고리
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
"id": "VyFk3RnfdN7deOBG",
"meta": {
"instanceId": "8443f10082278c46aa5cf3acf8ff0f70061a2c58bce76efac814b16290845177",
"templateCredsSetupCompleted": true
},
"name": "Time Tracking & Billing Automation:",
"tags": [],
"nodes": [
{
"id": "e62e8f45-3b5b-4a1d-a120-b0c07ebcc178",
"name": "워크플로우 '실행' 클릭 시",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-128,
0
],
"parameters": {},
"typeVersion": 1
},
{
"id": "fadb2658-c02d-4819-b6eb-12114cc58127",
"name": "메모지",
"type": "n8n-nodes-base.stickyNote",
"position": [
1120,
-352
],
"parameters": {
"width": 256,
"height": 336,
"content": "Combine Reminder & Invoice Data Streams\n\nAction: Merges reminder and invoice outputs for unified processing.\nDescription:\n\nCombines both branches (missing hours and invoice data).\n\nEnsures all team members get relevant communication.\n\nKeeps all data intact for the next reconciliation step."
},
"typeVersion": 1
},
{
"id": "4b5a9488-f7cd-436b-afaf-f698c756d933",
"name": "메모지1",
"type": "n8n-nodes-base.stickyNote",
"position": [
736,
-368
],
"parameters": {
"width": 288,
"height": 336,
"content": "Identify Issues with Missing Time Logs\n\nAction: Detects issues where time wasn’t logged.\nDescription:\n\nScans each user’s issues to find zero-hour entries.\n\nGenerates HTML reminder messages listing missing issues.\n\nCreates reminders only for users with actual missing logs.\n\nEnsures timely and accurate billing compliance."
},
"typeVersion": 1
},
{
"id": "174d0eec-6cce-4d0c-8b38-045b67f12f76",
"name": "메모지2",
"type": "n8n-nodes-base.stickyNote",
"position": [
368,
-368
],
"parameters": {
"width": 288,
"height": 336,
"content": "Aggregate Hours by Team Member\n\nAction: Groups Jira issues by each assignee and totals their hours.\nDescription:\n\nCalculates total logged hours per user.\n\nKeeps issue details like summary, status, and sprint.\n\nHandles missing data (unassigned or null time entries).\n\nProduces structured user-level data for invoices and reminders."
},
"typeVersion": 1
},
{
"id": "13457e31-0e46-48e0-88a1-91610a40b646",
"name": "메모지3",
"type": "n8n-nodes-base.stickyNote",
"position": [
80,
176
],
"parameters": {
"width": 304,
"height": 368,
"content": "Fetch All Project Issues with Time Data\n\nAction: Retrieves all Jira issues with time tracking info.\nDescription:\n\nUses Jira API to fetch every issue with getAll and returnAll.\n\nCollects essential fields like keys, summaries, time spent, and assignee.\n\nProvides project, sprint, and priority details.\n\nServes as the base dataset for billing and reporting."
},
"typeVersion": 1
},
{
"id": "edcde860-f8c8-4229-be41-c4c026be9a46",
"name": "메모지4",
"type": "n8n-nodes-base.stickyNote",
"position": [
720,
560
],
"parameters": {
"width": 304,
"height": 400,
"content": "Generate Invoice Summary with Text Attachment\n\nAction: Creates detailed text-based invoices per user.\nDescription:\n\nCalculates billing using a default hourly rate ($50/hour).\n\nLists each issue with hours and status.\n\nExports a plain-text invoice as .txt (base64 encoded).\n\nSkips users with zero hours to avoid empty invoices.\n\nSends structured JSON + binary data for email delivery"
},
"typeVersion": 1
},
{
"id": "d2450d54-f3e7-4732-bccd-2bc0e29b8238",
"name": "메모지5",
"type": "n8n-nodes-base.stickyNote",
"position": [
1760,
208
],
"parameters": {
"width": 272,
"height": 384,
"content": "Send Invoices & Reminders to Team\n\nAction: Sends personalized emails with invoices and reminders.\nDescription:\n\nSends to each developer’s email dynamically.\n\nIncludes name, project, and total hours in the message.\n\nAttaches generated invoice files.\n\nCan be extended to CC managers or finance.\n\nFinal delivery step for automated billing communication."
},
"typeVersion": 1
},
{
"id": "559bf371-f7b3-477f-82d9-a022ed54856f",
"name": "메모지6",
"type": "n8n-nodes-base.stickyNote",
"position": [
1424,
-384
],
"parameters": {
"width": 288,
"height": 368,
"content": "Reconcile JSON & Binary Attachments\n\nAction: Aligns JSON data with corresponding invoice files.\nDescription:\n\nMatches items using assignee name as identifier.\n\nFixes cases where JSON or binary is missing.\n\nGuarantees each record includes both email info and invoice.\n\nPrevents send failures and ensures complete data merging."
},
"typeVersion": 1
},
{
"id": "0da5f2fa-32f6-4c7e-85a0-474bf3550f4e",
"name": "시간 데이터가 포함된 모든 프로젝트 이슈 가져오기",
"type": "n8n-nodes-base.jira",
"position": [
176,
0
],
"parameters": {
"options": {},
"operation": "getAll",
"returnAll": true
},
"credentials": {
"jiraSoftwareCloudApi": {
"id": "199LdjjU3PhhL8xb",
"name": "saurabh jira"
}
},
"typeVersion": 1
},
{
"id": "76eca7bd-8b7d-4119-a18f-81ae5c142653",
"name": "팀원별 작업 시간 집계",
"type": "n8n-nodes-base.code",
"position": [
448,
0
],
"parameters": {
"jsCode": "// Get all Jira issues from input\nconst items = $input.all().map(i => i.json);\n\n// Initialize grouped output\nconst grouped = {};\n\n// Iterate over each issue\nfor (const issue of items) {\n const f = issue.fields || {};\n\n const assigneeName = f.assignee?.displayName || 'Unassigned';\n const email = f.assignee?.emailAddress || 'unknown@domain.com';\n const projectName = f.project?.name || 'Unknown Project';\n const sprint = f.customfield_10020?.[0]?.name || 'No Sprint';\n const timeSpentSeconds = f.timespent || 0;\n const timeSpentHours = (timeSpentSeconds / 3600).toFixed(2);\n\n if (!grouped[assigneeName]) {\n grouped[assigneeName] = {\n assignee: assigneeName,\n email,\n project_name: projectName,\n total_hours: 0,\n issues: []\n };\n }\n\n grouped[assigneeName].total_hours += parseFloat(timeSpentHours);\n grouped[assigneeName].issues.push({\n issue_key: issue.key,\n summary: f.summary,\n timespent_hours: timeSpentHours,\n status: f.status?.name || 'Unknown',\n priority: f.priority?.name || 'None',\n sprint,\n });\n}\n\n// Convert grouped object into array format\nconst output = Object.values(grouped).map(user => ({\n json: {\n assignee: user.assignee,\n email: user.email,\n project_name: user.project_name,\n total_hours: user.total_hours.toFixed(2),\n issues: user.issues,\n }\n}));\n\nreturn output;\n"
},
"typeVersion": 2,
"alwaysOutputData": false
},
{
"id": "3ef99004-e0bd-4346-8ac0-6e83a88260f0",
"name": "시간 기록이 누락된 이슈 식별",
"type": "n8n-nodes-base.code",
"position": [
832,
0
],
"parameters": {
"jsCode": "const users = $input.all().map(i => i.json);\n\nconst reminders = [];\n\nfor (const user of users) {\n const missing = user.issues.filter(i => parseFloat(i.timespent_hours) === 0);\n\n if (missing.length > 0) {\n const table = missing.map(i => \n `<tr>\n <td>${i.issue_key}</td>\n <td>${i.summary}</td>\n <td>${i.status}</td>\n <td>${i.priority}</td>\n <td>${i.sprint}</td>\n </tr>`\n ).join('');\n\n reminders.push({\n json: {\n assignee: user.assignee,\n email: user.email,\n missing_count: missing.length,\n project_name: user.project_name,\n html_message: `\n <p>Hi ${user.assignee},</p>\n <p>Our system found ${missing.length} issues where no time is logged:</p>\n <table border=\"1\" cellspacing=\"0\" cellpadding=\"5\">\n <tr><th>Issue Key</th><th>Summary</th><th>Status</th><th>Priority</th><th>Sprint</th></tr>\n ${table}\n </table>\n <p>Please log your hours for these issues before EOD to ensure accurate billing.</p>\n <p>Thank you,<br>Automation Bot 🤖</p>\n `\n }\n });\n }\n}\n\nreturn reminders;\n"
},
"typeVersion": 2
},
{
"id": "db74c4fe-3bf1-45f9-a971-64ffc74695df",
"name": "텍스트 첨부 파일이 포함된 청구서 요약 생성",
"type": "n8n-nodes-base.code",
"position": [
832,
384
],
"parameters": {
"jsCode": "// Combined Invoice Generator + Text File Export\n\nconst users = $input.all().map(i => i.json);\nconst RATE = 50; // hourly rate\nconst results = [];\n\nfor (const user of users) {\n const totalHours = parseFloat(user.total_hours || 0);\n if (totalHours === 0) continue;\n\n const totalAmount = totalHours * RATE;\n\n // Build the plain text table\n const issueLines = user.issues.map(i =>\n `${i.issue_key.padEnd(10)} | ${i.summary.padEnd(30)} | ${i.timespent_hours} hrs | ${i.status}`\n ).join('\\n');\n\n // Final plain-text invoice content\n const text = `\n===========================================\nINVOICE SUMMARY — ${user.project_name}\n===========================================\n\nAssignee: ${user.assignee}\nEmail: ${user.email}\nRate: $${RATE}/hour\n\n-------------------------------------------\nIssues\n-------------------------------------------\n${issueLines}\n\n-------------------------------------------\nTotal Hours: ${totalHours.toFixed(2)}\nTotal Amount: $${totalAmount.toFixed(2)}\n-------------------------------------------\n\nGenerated automatically by Techdome Billing Automation Bot.\n`;\n\n // Push JSON + Binary for email attachment\n results.push({\n json: {\n assignee: user.assignee,\n email: user.email,\n project_name: user.project_name,\n total_hours: totalHours.toFixed(2),\n total_amount: totalAmount.toFixed(2)\n },\n binary: {\n data: {\n fileName: `Invoice_${user.assignee}.txt`,\n mimeType: 'text/plain',\n data: Buffer.from(text, 'utf8').toString('base64')\n }\n }\n });\n}\n\nreturn results;\n"
},
"typeVersion": 2
},
{
"id": "064ef92a-f13c-4926-8526-a1eb398fa3db",
"name": "알림 및 청구서 데이터 스트림 결합",
"type": "n8n-nodes-base.merge",
"position": [
1216,
16
],
"parameters": {},
"typeVersion": 3.2
},
{
"id": "d3aec18e-05de-4ca9-8b19-263695b105b7",
"name": "JSON 및 바이너리 첨부 파일 조정",
"type": "n8n-nodes-base.code",
"position": [
1488,
16
],
"parameters": {
"jsCode": "// Combine JSON + Binary after Merge node\n// Safe for n8n v1.112.6 and later\n\nconst combined = [];\n\n// Get all incoming items\nconst items = $input.all();\n\n// We might have duplicates or partials, so we merge smartly\nfor (const item of items) {\n const json = item.json || {};\n const binary = item.binary || {};\n\n // Case 1: this item already has both\n if (json.email && binary.data) {\n combined.push({ json, binary });\n continue;\n }\n\n // Case 2: only has JSON\n if (json.email && !binary.data) {\n // Try to find its binary partner\n const match = items.find(\n (i) => i.binary && i.binary.data && i.json?.assignee === json.assignee\n );\n\n if (match) {\n combined.push({ json, binary: match.binary });\n } else {\n combined.push({ json }); // fallback\n }\n continue;\n }\n\n // Case 3: only has Binary\n if (binary.data && !json.email) {\n // Try to find its JSON partner\n const match = items.find(\n (i) => i.json && i.json.assignee && i.binary === undefined\n );\n if (match) {\n combined.push({ json: match.json, binary });\n } else {\n combined.push({ binary });\n }\n continue;\n }\n}\n\nreturn combined;\n"
},
"typeVersion": 2
},
{
"id": "94b59a38-4e45-47dc-8c3d-d133ddd07455",
"name": "팀에 청구서 및 알림 전송",
"type": "n8n-nodes-base.gmail",
"position": [
1808,
16
],
"webhookId": "0c82c299-6938-42ed-bda6-5007d79af34f",
"parameters": {
"sendTo": "={{ $json.email }}",
"message": "=Hi {{$json[\"assignee\"]}},<br><br>\nPlease find attached your invoice summary for <strong>{{$json[\"project_name\"]}}</strong>.<br><br>\n📊 <b>Total Hours:</b> {{ $('Aggregate Hours by Team Member').item.json.total_hours }}<br>\nRegards,<br>\nTechdome Billing Automation Bot\n",
"options": {
"attachmentsUi": {
"attachmentsBinary": [
{}
]
}
},
"subject": "={{ $json.project_name }}"
},
"credentials": {
"gmailOAuth2": {
"id": "RchiXdmY8WaQhOSJ",
"name": "Gmail account"
}
},
"typeVersion": 2.1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "c7c9ac45-c34e-4b9e-a15d-0aea4da1f6d6",
"connections": {
"76eca7bd-8b7d-4119-a18f-81ae5c142653": {
"main": [
[
{
"node": "3ef99004-e0bd-4346-8ac0-6e83a88260f0",
"type": "main",
"index": 0
},
{
"node": "db74c4fe-3bf1-45f9-a971-64ffc74695df",
"type": "main",
"index": 0
}
]
]
},
"94b59a38-4e45-47dc-8c3d-d133ddd07455": {
"main": [
[]
]
},
"d3aec18e-05de-4ca9-8b19-263695b105b7": {
"main": [
[
{
"node": "94b59a38-4e45-47dc-8c3d-d133ddd07455",
"type": "main",
"index": 0
}
]
]
},
"e62e8f45-3b5b-4a1d-a120-b0c07ebcc178": {
"main": [
[
{
"node": "0da5f2fa-32f6-4c7e-85a0-474bf3550f4e",
"type": "main",
"index": 0
}
]
]
},
"3ef99004-e0bd-4346-8ac0-6e83a88260f0": {
"main": [
[
{
"node": "064ef92a-f13c-4926-8526-a1eb398fa3db",
"type": "main",
"index": 0
}
]
]
},
"064ef92a-f13c-4926-8526-a1eb398fa3db": {
"main": [
[
{
"node": "d3aec18e-05de-4ca9-8b19-263695b105b7",
"type": "main",
"index": 0
}
]
]
},
"0da5f2fa-32f6-4c7e-85a0-474bf3550f4e": {
"main": [
[
{
"node": "76eca7bd-8b7d-4119-a18f-81ae5c142653",
"type": "main",
"index": 0
}
]
]
},
"db74c4fe-3bf1-45f9-a971-64ffc74695df": {
"main": [
[
{
"node": "064ef92a-f13c-4926-8526-a1eb398fa3db",
"type": "main",
"index": 1
}
]
]
}
}
}자주 묻는 질문
이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
중급 - 문서 추출
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
API 속도 제한 및 인증 FAQ 테스트
GPT-4o-mini, Google 시트 및 Slack 알림을 사용한 API FAQ 품질 테스트 자동화
If
Set
Code
+
If
Set
Code
19 노드Rahul Joshi
문서 추출
Monday.com 및 Jira에서 Outlook으로의 AI 기반 피드백 분류 및 보고
Azure GPT-4, Jira 작업, Outlook 보고서를 사용한 Monday.com의 고객 피드백 분석
Set
Code
Jira
+
Set
Code
Jira
27 노드Rahul Joshi
반송 및 무효 감지 (Gmail 트리거)
Gmail, Google Sheets 및 Slack을 사용한 이메일 바운스 및 무효 주소 감지 자동화
Code
Cron
Gmail
+
Code
Cron
Gmail
26 노드Rahul Joshi
신입 개발자 온보딩 자동화
GPT-4o를 사용한 직원 온보딩 자동화: Jira, Notion, Gmail 통합
If
Set
Code
+
If
Set
Code
21 노드Rahul Joshi
인사
사건 관리 워크플로
Jira, Slack, Google Sheets 및 Drive를 통한 사건 대응 자동화
If
Set
Code
+
If
Set
Code
23 노드Rahul Joshi
데브옵스
## 자체 호스팅 N8N 사용자 전용:
GPT-4o-mini, Google Sheets, Gmail을 사용한 Zendesk 지원 응답 자동화
Code
Gmail
Merge
+
Code
Gmail
Merge
24 노드Rahul Joshi
콘텐츠 제작
워크플로우 정보
난이도
중급
노드 수15
카테고리1
노드 유형6
저자
Rahul Joshi
@rahul08Rahul Joshi is a seasoned technology leader specializing in the n8n automation tool and AI-driven workflow automation. With deep expertise in building open-source workflow automation and self-hosted automation platforms, he helps organizations eliminate manual processes through intelligent n8n ai agent automation solutions.
외부 링크
n8n.io에서 보기 →
이 워크플로우 공유