Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions src/lib/server/sheets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,43 @@ async function getToken(credJson: string): Promise<string> {
return token;
}

async function sheetIsEmpty(
spreadsheetId: string,
range: string,
token: string
): Promise<boolean> {
const url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${range}`;
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
if (!res.ok) return true;
const body = await res.json();
return !body.values || body.values.length === 0;
}

export const HEADER_ROW = [
'Date', 'Shift', 'Type', 'Calc ID',
'Gross Tips', 'CC Fee Rate', 'CC Fees', 'Net Tips',
'Kitchen %', 'Kitchen Pool', 'Liquor Sales', 'Bar Liquor %', 'Bar Pool', 'FOH Pool',
'Name', 'Role', 'FOH Share', 'Bar Share', 'Kitchen Share', 'Total',
];

/**
* Append rows to a Google Sheet.
* Values are plain strings/numbers; Sheets will auto-detect dates and numbers.
* Writes the header row first only when the sheet is empty.
*/
export async function appendToSheet(
spreadsheetId: string,
sheetName: string,
values: (string | number)[][],
dataRows: (string | number)[][],
credJson: string
): Promise<void> {
const token = await getToken(credJson);
const range = encodeURIComponent(`'${sheetName}'`);
const url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${range}:append?valueInputOption=USER_ENTERED&insertDataOption=INSERT_ROWS`;
const range = encodeURIComponent(`'${sheetName}'!A1`);
const base = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}`;

const empty = await sheetIsEmpty(spreadsheetId, range, token);
const values = empty ? [HEADER_ROW, ...dataRows] : dataRows;

const url = `${base}/values/${range}:append?valueInputOption=USER_ENTERED&insertDataOption=INSERT_ROWS`;
const res = await fetch(url, {
method: 'POST',
headers: {
Expand Down
28 changes: 18 additions & 10 deletions src/routes/api/export/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,13 @@ export const POST: RequestHandler = async ({ request, locals }) => {
const credJson = process.env.GOOGLE_SERVICE_ACCOUNT_JSON;
if (!credJson) error(400, 'GOOGLE_SERVICE_ACCOUNT_JSON env var not set.');

// Build the row matching PRD OR-8
const headerRow = [
'Date', 'Shift', 'Gross Tips', 'CC Fee Rate', 'CC Fees', 'Tips After Fees',
'Kitchen %', 'Kitchen Pool', 'Liquor Sales', 'Bar Liquor %', 'Bar Pool', 'FOH Pool',
...dists.map(d => d.name),
];
// Columns: Date, Shift, Type, Calc ID,
// Gross Tips, CC Fee Rate, CC Fees, Net Tips,
// Kitchen %, Kitchen Pool, Liquor Sales, Bar Liquor %, Bar Pool, FOH Pool,
// Name, Role, FOH Share, Bar Share, Kitchen Share, Total

const dataRow = [
calc.date, calc.shift,
const summaryRow: (string | number)[] = [
calc.date, calc.shift, 'summary', calc.id,
formatCents(calc.gross_tips_cents),
`${(calc.cc_fee_rate * 100).toFixed(1)}%`,
formatCents(calc.cc_fees_cents),
Expand All @@ -45,14 +43,24 @@ export const POST: RequestHandler = async ({ request, locals }) => {
`${(calc.bar_liquor_pct * 100).toFixed(0)}%`,
formatCents(calc.bar_pool_cents),
formatCents(calc.foh_pool_cents),
...dists.map(d => formatCents(d.total_cents)),
'', '', '', '', '', '',
];

const staffRows: (string | number)[][] = dists.map(d => [
calc.date, calc.shift, 'staff', calc.id,
'', '', '', '', '', '', '', '', '', '',
d.name, d.role,
formatCents(d.foh_share_cents),
formatCents(d.bar_pool_share_cents),
formatCents(d.kitchen_share_cents),
formatCents(d.total_cents),
]);

try {
await appendToSheet(
spreadsheetId,
settings.google_sheets_sheet_name || 'Tip History',
[headerRow, dataRow],
[summaryRow, ...staffRows],
credJson
);
} catch (e) {
Expand Down