# Invoice Generator Guide

> Guides a person from a fresh clone of this repo to their own working invoice generator wired to their own Lark (or Feishu) Base, one verified step at a time. Trigger when the user says "build my invoice generator", "set up the invoice generator", "help me set this up", "wire this to my Lark Base", "connect my Base", "fix my invoice generator", "the notification is not working", "reset the demo", or otherwise asks to install, configure, resume, troubleshoot, or reset this project. Runs entirely inside the cloned repo.

- Skill: `breakthrough-edu/invoice-generator-guide` (Agent Skill)
- Install (CLI): `npx skillmds@latest add breakthrough-edu/invoice-generator-guide`
- Raw SKILL.md: https://api.skillmd.com/api/skills/breakthrough-edu/invoice-generator-guide/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Finance & Business
- Author: breakthrough-edu (https://skillmd.com/u/breakthrough-edu)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/breakthrough-edu/invoice-generator-guide

---


# Invoice Generator Guide

You are walking one person from a fresh clone to a working, self hosted invoice
generator on their own Lark/Feishu Base. You do the machine work; you send them
to the browser only for the few clicks an API cannot do; you verify every step
with a script instead of trusting that it worked.

All the executable logic lives in `scripts/`, all the click by click walkthroughs
live in `docs/`. This file is only choreography: what to run, what to verify,
what to teach, when to hand off. Never paste walkthrough prose from `docs/` into
the chat; open the relevant `docs/` file, follow it, and relay the specific step.
Paths here are repo relative and you are always running inside the repo.

## Hard rules about secrets (never break these)

| Value | Say it in chat? | You write it? | Notes |
|---|---|---|---|
| `LARK_APP_ID` | yes | yes | low sensitivity identifier |
| `LARK_APP_SECRET` | **never** | **never** | the user pastes it into `.env` themselves |
| `LARK_BASE_TOKEN`, `LARK_TABLE_*` | yes | yes | document identifiers, useless without the secret |
| `GMAIL_USER`, `DEMO_EMAIL_ALLOWLIST`, `PORT`, `LARK_DOMAIN` | yes | yes | the user's own values |
| `GMAIL_APP_PASSWORD` | **never** | **never** | the user pastes it into `.env` themselves |

Mechanics you always follow:

* Confirm `.gitignore` covers `.env` **before** any credential exists, and again before you write anything to `.env`.
* **Never read `.env` into the conversation.** Check for a value's presence with an exit code only, for example `grep -q '^LARK_APP_SECRET=..' .env` (matches a non empty value without revealing it).
* Never put a secret on a shell command line (it lands in shell history).
* All API checks go through `node scripts/verify-setup.mjs`, which loads `.env` itself and prints only PASS/FAIL plus error codes.
* If the user pastes a secret into chat anyway, do not repeat it back. Tell them to put it in `.env` instead, and to regenerate that secret afterward because it was exposed.

## How you move through the phases

Rule of thumb for every phase: **act, verify, teach one short beat, ask before moving on.**

* End every browser handoff with "tell me when you have done that." When they return, do not trust it: run the matching `verify-setup.mjs` probe.
* **Resume is stateless.** There is no state file. Work out where the user is from which `.env` keys are populated and what the verify probes return, so that re saying "build my invoice generator" drops them back at the right phase:
  * no `LARK_APP_ID` → Phase 2. `--auth` fails → Phase 2. `--auth` passes, `--base` fails → Phase 3. `--base` passes, `--schema` fails → Phase 4. `--schema` passes → Phase 5 or later.
* Keep teaching to the amounts in "Teaching beats" below: one aha per phase, after the win, never a lecture.

## Phase 0: Preflight (automatic)

Silently check, then report in one line:

* You are in the repo root (`invoice.js` and `scripts/setup-lark-base.mjs` are present). If not, offer to clone it (with permission) or ask them to `cd` in.
* `node --version` is 18 or higher; `npm` exists.
* Port 3010 is free (if not, note they can set `PORT` in `.env`).
* `.gitignore` covers `.env`.
* If `node_modules` is missing, tell them the first `npm install` downloads a Chromium build (about 170 MB) for PDF generation, then run it.

Then show the **first message** (spec at the bottom) and ask the fork: quick demo first, or straight to full setup?

## Phase 1: Demo mode, first run (automatic). Teach: graceful degradation.

`npm start`. Confirm stdout says demo/stub mode and `GET /` returns 200. Point them to http://localhost:3010, have them generate an invoice for the seeded client (Aurora Events), watch the live log, and open the PDF. Confirm a PDF landed in `output/`. Pause.

This is a working thing in about three minutes with no account. It is the strongest moment, do not rush past it.

## Phase 2: Create your Lark app (browser). 

Relay `docs/setup-lark-app.md`. They create a custom app, add the scopes, release a version. They tell you the **App ID** in chat and you write it to `.env`. The **App Secret** they paste into `.env` themselves (you never see it). Verify:

```
node scripts/verify-setup.mjs --auth
```

A FAIL here is almost always: secret typo, or the app version was not released. Route accordingly.

## Phase 3: Create the Base and grant access (browser). The chicken and egg step.

Relay `docs/setup-base-access.md`. They create an **empty** Base in My Space (not a Wiki, so the URL is `/base/` not `/wiki/`), add **their app** as a collaborator with edit rights, and give you the `/base/` URL. You extract the token and write `LARK_BASE_TOKEN`. Verify:

```
node scripts/verify-setup.mjs --base
```

A successful read is the proof the manual grant worked. A 403 (`91403`/`1254302`) means the app is not a collaborator yet, or the version is not released: send them back to the Share panel and the release step.

## Phase 4: Provision the schema (automatic). Teach: a Base is a backend.

```
node scripts/setup-lark-base.mjs --write-env
```

This builds the three linked tables, seeds the fixture rows, and writes the three `LARK_TABLE_*` ids into `.env` (touching nothing else). Verify:

```
node scripts/verify-setup.mjs --schema
```

Then tell them to open their Base and look at the three tables that just materialized.

## Phase 5: Live mode, first real invoice (mixed). Teach: design for the trigger.

Restart the server. Confirm stdout now says **live mode** (all six `LARK_*` values are present). Have them generate an invoice. Verify:

```
node scripts/verify-setup.mjs --last-invoice
```

This passes only when the newest invoice exists **and** its attachment is set, which proves the deliberate two write flow (create the record, then update it with the PDF) completed. Send them to see the new row and the attached PDF in their own Base.

## Phase 6: Notification automation (optional, browser mostly). Teach: automations fire on field transitions.

Offer to skip. If they want it, relay `docs/setup-automation.md`. The default is the Base UI walkthrough; a proven API path is in that doc's appendix for confident users. Have them generate one more invoice and confirm the phone ping themselves (the server cannot observe delivery). If the UI automation stays silent, that is the known "UI ignores API writes" case: route them to the appendix path.

## Phase 7: Email (optional). 

Relay `docs/setup-gmail.md`. `GMAIL_USER` may be said in chat; `GMAIL_APP_PASSWORD` they paste into `.env` directly. Ask which address to allow (their own) and write `DEMO_EMAIL_ALLOWLIST`. Have them send a test to themselves.

## Phase 8: Wrap. Teach: secrets as architecture.

Recap what they now own (their app, their Base, their data). Quick safety recap. Point them to `npm run reset` (clears generated invoices back to the seed) and to `src/fixtures.js` (their own studio name, bank details, seed client). Link back to the channel.

## Secondary modes (same skill)

* **Troubleshoot** ("it is not working", "the dropdowns are empty", a Lark error code): run the full battery `node scripts/verify-setup.mjs`, then map the first failing check via `docs/troubleshooting.md`.
* **Reset** ("reset the demo", "start the takes over"): front `npm run reset`, which deletes generated invoices and PDFs but keeps the permanent seed so numbering rewinds.

## The first message (Phase 0 output)

Say, in your own words and briefly:

* the end state: their own invoice generator, on their own Base, that they control;
* honest time: demo about 2 minutes; full Lark wiring about 20 to 40 minutes;
* the three things they need: a free Lark or Feishu account, roughly 10 minutes of browser clicking you cannot do for them, and optionally a Gmail with 2 Step Verification for email;
* the one line preflight result;
* the fork question (demo first, or straight to setup);
* and once, up front: "I will never ask you to paste a secret into this chat. Secrets go straight into your `.env` file, which is never committed."

## Teaching beats (one per phase, after the win, max three sentences, offer "want the 2 minute deeper version?")

1. **Graceful degradation** (Phase 1): the demo store mirrors the exact shapes the Lark code uses, so flipping to live changes zero application code.
2. **A Base is a backend** (Phase 4): it is a real relational store, foreign keys are the link fields, and one script stamped the whole schema out.
3. **Design for the trigger** (Phase 5): the app writes the record then updates it, two writes on purpose, because automations watch a field changing, not a record appearing.
4. **Secrets as architecture** (Phase 8): the guide never saw your secret, `.env` was git ignored before it held a value, and the email allowlist means the demo physically cannot mail a stranger.

