# v10: T D1   t Xi! L Freee pt0| <\ 8@ Vertex AI\ XX, Google Sheets X D\ ܤ\D l. t 줸  ܤ\D X, 4 \`  T )D LD. ##  ܤ\ DM } ```   Freee API  OAuth2 x ( p 1)     p p  t p pt0     Vertex AI  Gemini 2.5 Pro\ tL X  (Gemini)  (D, PD,  )     Google Sheets   p   (tL h)  ``` ## D1 | lp ``` /20251126-googlesheet-vertexai-freee-script  /src   index.ts # Tx i l   testVertexAI.ts # Vertex AI L    freeeClient.ts # Freee API t|t   googleSheetClient.ts # Google Sheets API t|t   vertexAiClient.ts # Vertex AI t|t   tokenManager.ts # p l   interfaces.ts # TypeScript x0t X  /credentials   freee-sheets-service-key.json # Google D   .env # X   tokens.json # 1 p |  package.json  tsconfig.json  .gitignore ``` ##   L 9\ <\ 0T| `  : ```bash npx ts-node src/index.ts ``` ## T ) ### ) 1: Cron (Linux/Mac) | $ 9 <\ X] $: ```bash # crontab crontab -e # L |x (| $ 9 ) 0 9 * * * cd /path/to/project && npx ts-node src/index.ts >> /path/to/logs/freee-sync.log 2>&1 ``` \ |D UxX | 0`  : ```bash tail -f /path/to/logs/freee-sync.log ``` ### ) 2: GitHub Actions GitHub T| xX, GitHub Actions\ T: ```yaml # .github/workflows/sync-freee.yml name: Freee to Google Sheets Sync on: schedule: # | $ 9 (UTC 0 0) - cron: '0 0 * * *' workflow_dispatch: #  jobs: sync: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: npm install - name: Create credentials file run: | mkdir -p credentials echo '${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }}' > credentials/freee-sheets-service-key.json - name: Create .env file run: | echo "FREEE_CLIENT_ID=${{ secrets.FREEE_CLIENT_ID }}" >> .env echo "FREEE_CLIENT_SECRET=${{ secrets.FREEE_CLIENT_SECRET }}" >> .env echo "FREEE_BUSINESS_ID=${{ secrets.FREEE_BUSINESS_ID }}" >> .env echo "FREEE_REFRESH_TOKEN=${{ secrets.FREEE_REFRESH_TOKEN }}" >> .env echo "GOOGLE_DOCUMENT_ID=${{ secrets.GOOGLE_DOCUMENT_ID }}" >> .env echo "GOOGLE_SERVICE_ACCOUNT_KEY_PATH=./credentials/freee-sheets-service-key.json" >> .env - name: Run sync run: npx ts-node src/index.ts ``` GitHub Secrets LD : - `FREEE_CLIENT_ID` - `FREEE_CLIENT_SECRET` - `FREEE_BUSINESS_ID` - `FREEE_REFRESH_TOKEN` - `GOOGLE_DOCUMENT_ID` - `GOOGLE_SERVICE_ACCOUNT_KEY` ### ) 3: Cloud Run / Cloud Functions Google Cloud \ : ```typescript // functions/index.ts import { getDeals } from '../src/freeeClient'; import { appendFreeeDeals } from '../src/googleSheetClient'; import { classifyTransactionsBatch } from '../src/vertexAiClient'; export const syncFreeeToSheets = async (req: any, res: any) => { try { const companyId = parseInt(process.env.FREEE_BUSINESS_ID || '', 10); const today = new Date(); const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1); const startDate = firstDayOfMonth.toISOString().split('T')[0]; const endDate = today.toISOString().split('T')[0]; const deals = await getDeals(companyId, startDate, endDate); const categories = await classifyTransactionsBatch(deals); const result = await appendFreeeDeals(deals, categories); res.json({ success: true, added: result.added, skipped: result.skipped, }); } catch (error: any) { console.error('Error:', error.message); res.status(500).json({ success: false, error: error.message }); } }; ``` Cloud Scheduler\ | ] $`  . ##  0 ### \ Ux \ X %\, |\ Xp t| \E D| `  : ```bash npx ts-node src/index.ts | tee -a logs/sync-$(date +%Y%m%d).log ``` ### $X L \ $X  tT|t Slack<\ LD ] $: ```typescript // src/index.ts import axios from 'axios'; const sendSlackNotification = async (message: string) => { const webhookUrl = process.env.SLACK_WEBHOOK_URL; if (!webhookUrl) return; try { await axios.post(webhookUrl, { text: message }); } catch (error) { console.error('Slack L  (:', error); } }; // main hX catch ] 8 catch (error: any) { const errorMessage = `L Freee 0T (: ${error.message}`; console.error(errorMessage); await sendSlackNotification(errorMessage); throw error; } ``` ## D \T ### Vertex AI D  1. **x **: `gemini-2.5-pro` `gemini-1.5-flash` (T 4) 2. ****: |\  t t X 3. **0X **: p| \ X l\ X ```typescript //  const categoryCache = new Map(); export const classifyTransactionWithCache = async (deal: any): Promise => { const cacheKey = `${deal.partner_id}-${deal.ref_number}`; if (categoryCache.has(cacheKey)) { console.log(` : ${cacheKey}`); return categoryCache.get(cacheKey)!; } const category = await classifyTransaction(deal); categoryCache.set(cacheKey, category); return category; }; ``` ## H m 1. **X **:  `.env` |D Git X 8 2. **p 8**: `tokens.json` `.gitignore` h 3. **D **: Google D  \ \ 4. **0 **: 0<\ API \ Ux ## 8 t ### p $X ```bash # tokens.jsonD X x rm tokens.json npx ts-node src/freeeClient.ts ``` ### Vertex AI D  ```typescript // || \ t \ const MAX_DAILY_TRANSACTIONS = 100; if (deals.length > MAX_DAILY_TRANSACTIONS) { deals = deals.slice(0, MAX_DAILY_TRANSACTIONS); console.log(` || \<\ ${MAX_DAILY_TRANSACTIONS}t i.`); } ``` ### Google Sheets \ Google Sheets \ 500 @L i. pt0 Dt \ ܸ| X8: ```typescript const sheetName = `Freeep_${today.getFullYear()}_${String(today.getMonth() + 1).padStart(2, '0')}`; ``` ## 4 t D T Freee pt0 ܤ\t D1ȵ! **u 1:** -  Freee API OAuth2 x  p 1 -  Vertex AI (Gemini)| \ AI X -  Google Sheets 0T   p -  D 4x T t ܤ\D 0<\ \ Ut i: - / 1 -   L - pt0 T - x D@X i \| D1X D Xܽ!