# /sales-doc

> Generate a sales offer, invoice, or contract for a ACME Agency prospect/client as a Google Doc, store it in the correct 2026 Drive folder (Ponude / Računi / Ugovori), and post the link to the #your-channel Slack channel.


# /sales-doc — offer, invoice, contract generator

## Purpose

Sales closers at ACME Agency need to produce one of three Google Docs fast, right after a call:

1. **Ponuda** (offer) — proposal with services + pricing
2. **Račun** (invoice) — monthly billing with EUR total + BAM conversion
3. **Ugovor** (contract) — business-cooperation contract ("Ugovor o poslovnoj saradnji")

Every doc is based on a tokenized master template on Drive. The skill copies the template, replaces `{{PLACEHOLDERS}}`, optionally deletes unused table rows, publishes the doc, and posts the link to `#your-channel`. The closer then opens the doc, fine-tunes anything specific, and downloads as PDF from the Docs UI.

## When to trigger

- User says: "create offer for X", "napravi ponudu za X", "make an offer for X"
- User says: "create invoice for X", "napravi račun za X", "make invoice for X"
- User says: "create contract for X", "napravi ugovor za X"
- User invokes `/sales-doc` directly

Do **not** trigger for: general doc writing, ClickUp tasks, copywriting, or anything not in the three modes above.

## Three modes

### Mode A — Offer (`--mode offer`)

The offer renders from a single shell template (`offer_shell`) whose body is assembled at runtime from the module catalog in `service_modules.json`. The only fixed parts of every offer are the header, the pricing table, and the footer — everything in between is dynamically composed from the `--modules` list. No manual post-edits needed.

**What you extract from the brief:**
- `--client "Display Name"` — appears in the header, e.g. "AGS Group d.o.o."
- `--service-tag "..."` — short label on the right side of the header. Auto-derives from modules if omitted.
- `--modules comma,separated` — pick from the module catalog below.
- `--module-params "mod.key=value,mod.key=value"` — per-module parameters (e.g. inbox count, prospect count for cold outbound).
- `--monthly N` — monthly retainer in EUR
- `--setup N` — one-time setup fee in EUR
- `--budget-min N` — recommended minimum ad budget, only shown for paid-ad offers (default 700)
- `--date DD.MM.YYYY.` — doc date (default today, Croatian format with trailing dot)
- `--sender faris|adi` — signature block
- `--tier lead_engine|growth|scale|partner` — **productized package shortcut** (see below). Auto-fills `--monthly`, `--setup`, `--budget-min`, and `--modules` from `tiers.json`, and injects the bounded scope + cadence. Any explicit flag still overrides the tier default.

### Productized tiers (`--tier`)

The four standard packages live in `.claude/skills/sales-doc/tiers.json` (single source of truth — edit numbers there). Each tier defines **bounded** deliverables (e.g. "do 4 vizuala mjesečno", "0 video oglasa") and a **communication cadence** (channel / calls per month / response SLA / reporting). This is what stops scope creep: the offer states a number, not "daily management".

| `--tier` | HR aliases | Monthly | Setup | Min budget | Modules |
|---|---|---|---|---|---|
| `lead_engine` | `pokretač`, `engine` | 690 | 390 | 500 | meta_ads, crm (1 channel) |
| `growth` | `rast` | 1.090 | 590 | 1.000 | + landing, email, tracking |
| `scale` | `skaliranje` | 1.790 | 890 | 2.000 | both channels + funnel |
| `partner` | `dominacija` | 3.900 | 1.500 | 4.000 | + social media + newsletters (3 slots) |

When `--tier` is passed (or auto-detected from price — see below), the offer renders a **`{{PACKAGE_BLOCK}}`** section (OPSEG PAKETA + KOMUNIKACIJA + DODATNE USLUGE add-on price list) before the price table, and the contract renders **Član 4 — OPSEG USLUGA I DODATNE USLUGE** (`{{PACKAGE_ANNEX}}`: bounded scope + comms cadence + out-of-scope clause + add-on cjenovnik) right after the price article. Both fill from `tiers.json`. The offer block is empty on non-tier offers; the contract's Član 4 falls back to a "scope is defined in the offer" sentence so the article is never empty. The à-la-carte add-on list also lives in `tiers.json` for the "recharge when they ask for too much" mechanism.

**Template placeholders** are inserted by `node ACME Agency/scripts/<id>.mjs` (idempotent; `--revert` removes them). The contract's scope was promoted from an end-of-doc annex to a numbered **Član 4** (articles renumbered Č4→Č11) by `node ACME Agency/scripts/<id>.mjs` (one-time, idempotent). Re-run either only if a template is recreated.

**Auto-detect + pricing flags:**
- **Auto-detect package from price:** if `--tier` is omitted but `--monthly` is within ~15% (or 150 EUR) of a package, the scope is added automatically — so a closer who just enters a price close to a package still gets the scope in the offer/contract. Pass **`--custom`** to disable this for a genuinely bespoke deal.
- **Setup pricing:** standard `setup = monthly` (1×, full, no discount). A discount is discretionary — pass **`--setup-discount <pct>`** (e.g. `50` = half off to close today, also `40`/`30`) and it recomputes the setup. No flag = no discount.

Full reasoning + closer talk-track + the **onboarding call SOP** + the **pitch brochure** (HTML→PDF) live in `ACME Agency/strategy/` and the internal Drive folder "ACME Agency — Agency Self / Paketi i Pozicioniranje 2026". Regenerate the strategy/one-pager/SOP docs with `node ACME Agency/strategy/build_package_docs.mjs` and the brochure with `node ACME Agency/strategy/build_pitch_brochure.mjs`.

**Module catalog (`service_modules.json`):**

| Module id | Offer section (numbered in doc) |
|---|---|
| `meta_ads` | Performance marketing — Meta bullets |
| `google_ads` | Performance marketing — Google bullets |
| `landing` | Izrada prodajne stranice |
| `crm` | Integracija sa CRM platformom i buking kalendarom (full) |
| `email` | Email automatizacija (sales-pipeline triggers) |
| `tracking` | Tracking setup — bullet flavor picked by which ad platforms are in modules |
| `cold_email_accounts` | Kreiranje novih email računa — params: `inbox_count` (default 30) |
| `cold_email_list` | Generisanje email liste — params: `prospects_count` (default 2.000) |
| `cold_email_campaign` | Kreiranje cold email kampanje |
| `cold_crm` | CRM za praćenje upita (short cold-outbound version) |
| `linkedin` | Kreiranje Cold LinkedIn kampanje — becomes a bullet section, NOT a line-item price |

Legacy aliases: `meta` → `meta_ads`, `google` → `google_ads` (accepted for backwards-compat).

**Section ordering:**
- **Paid-ads offer** (`meta_ads` / `google_ads` present): performance marketing → landing → crm → email → tracking → linkedin
- **Cold-outbound offer** (any `cold_email_*` or `cold_crm` or `linkedin` without paid ads): `cold_email_accounts` → `cold_email_list` → `cold_email_campaign` → `cold_crm` → `linkedin`

**Mapping natural-language briefs → flags:**

| Closer phrase | Modules |
|---|---|
| "Meta + Google, full stack" | `meta_ads,google_ads,landing,crm,email,tracking` |
| "Meta only" | `meta_ads,landing,crm,email,tracking` |
| "Google only" | `google_ads,landing,crm,email,tracking` |
| "Just ads, no landing page" | `meta_ads,google_ads,tracking` |
| "Cold outbound + LinkedIn" | `cold_email_accounts,cold_email_list,cold_email_campaign,cold_crm,linkedin` |
| "LinkedIn outbound only" | `linkedin,cold_crm` |

If the closer doesn't specify `--service-tag`, derive from modules:
- only meta → `Meta Ads`
- only google → `Google Ads`
- both → `Performance marketing`
- cold email → `Cold outbound`
- linkedin only → `LinkedIn Outbound`

### Mode B — Invoice (`--mode invoice`)

**What you extract:**
- `--client "Display Name"`
- `--items "Label1:amount,Label2:amount,..."` — up to 3 items. Typical labels: `Mjesečne usluge`, `Setup fee`, `Budžet`, `Budžet Njemačka`, `Budžet Hrvatska + Slovenija`
- `--date DD.MM.YYYY.` — invoice date (default today)
- `--invoice-number DDxxMM/YY` — override auto-numbering (only if closer insists)
- `--bf NNNNNN` — override the auto BF. Default BF = the invoice number with the `/YY` stripped (e.g. invoice `190304/26` → BF `190304`). Closer only passes this when accounting needs a specific value.

**First-invoice rule:** if the closer says "first invoice", "prvi račun", "onboarding invoice", or this is clearly the first billing cycle for a new client (same month as the offer signing) AND the offer had a setup fee, **include a `Setup fee` line in `--items`** with the same amount as the offer's setup. Example — closer says "first invoice for Cekić, 1100 monthly" after an offer with 1100 setup → `--items "Mjesečne usluge:1100,Setup fee:1100"`. Subsequent monthly invoices omit setup.

Legal details (OIB, address) auto-resolve for Croatian clients. Skill fails fast with clear list of missing fields if they can't be resolved — closer either adds them to `ACME Agency/clients/legal.json` or passes override flags:
- `--legal-name "CLIENT d.o.o."` (defaults to --client)
- `--oib <id>`
- `--address-l1 "Ulica Broj 1"`
- `--address-l2 "10000 Zagreb, Hrvatska"`

**Examples:**

| Closer phrase | Flags |
|---|---|
| "Invoice for ACME Agency, 1250 mjesečno, BF 4511" | `--client "ACME Agency" --items "Mjesečne usluge:1250" --bf 4511` |
| "Račun za ACME Agency, 1000 mjesečno, budžet Njemačka 1750, budžet HR+SLO 300, BF 4933" | `--client "ACME Agency" --items "Mjesečne usluge:1000,Budžet Njemačka:1750,Budžet Hrvatska + Slovenija:300" --bf 4933` |
| "Invoice Perfect Solution, 550 mjesečno + 550 setup + 600 budžet, BF 5349" | `--client "Perfect Solution" --items "Mjesečne usluge:550,Setup fee:550,Budžet:600" --bf 5349` |

### Mode C — Contract (`--mode contract`)

**What you extract:**
- `--client "Display Name"`
- `--monthly N` — monthly EUR retainer
- `--setup N` — one-time setup fee EUR
- `--tier lead_engine|growth|scale|partner` — defaults `--monthly`/`--setup`/`--services` from `tiers.json` and **prints the recommended Opseg + Radovi-izvan-opsega clauses** for the closer to paste into the contract (so scope is defined contractually).
- `--services "..."` — short Croatian summary for Član 3, e.g. "Meta Ads, Google Ads i landing page" or "social media management, Google Ads i cold outbound aktivnosti". Default: "social media management, Google Ads i Meta Ads aktivnosti" (or the tier's module note when `--tier` is passed).
- `--contract-date DD.MM.YYYY.` — (default today)
- `--contract-city "Sarajevo"` — place of signing (default Sarajevo)
- Legal details: same auto-resolution + override flags as Mode B, plus:
  - `--rep-name "Goran Leutar"`
  - `--rep-role direktor|vlasnik|prokurista` (default direktor)

If legal info is missing after auto-resolution, skill prints a clear message listing which fields are unresolved and exits — never writes a half-filled contract.

## How the skill runs

1. Parse flags (or natural-language brief → flags via Claude).
2. Load `config.json`; fail if template ID for the mode is `null` (setup script needs to run first).
3. Look up client legal details (invoice + contract modes only).
4. Compute derived values: total EUR, BAM conversion, invoice number, assembled services block.
5. Copy template → destination folder with a dated title.
6. `replaceAllText` for every token.
7. Delete empty table rows via `{{__EMPTY__}}` sentinel (invoice items, offer extra line).
8. `makePublic` + get URL.
9. Post one message to `#your-channel` with the link.

## Setup

Before first use, run the template setup sequence (order matters — shell depends on offer_standard):

```
node ACME Agency/scripts/<id>.mjs
node ACME Agency/scripts/setup_sales_doc_shell.mjs
```

The first script creates a `Sales templates/` subfolder in Klijenti on Drive and produces invoice + contract + original offer templates with `{{TOKENS}}`. The second script derives a single "shell" offer template from the standard offer (hardcoded services block collapsed into `{{SERVICES_BLOCK}}`, header service label to `{{SERVICE_TAG}}`, budget note to `{{BUDGET_NOTE_BLOCK}}`). Template IDs are written to `.claude/skills/sales-doc/config.json`.

## Invocation examples

```
node .claude/skills/sales-doc/script.mjs --mode offer \
  --client "AGS Group d.o.o." \
  --modules meta_ads,google_ads,landing,crm,email,tracking \
  --monthly 1000 --setup 1000 --budget-min 700

# Cold outbound + LinkedIn example (DBCI 2026-04-24)
node .claude/skills/sales-doc/script.mjs --mode offer \
  --client "D-Business Consulting International d.o.o." \
  --modules cold_email_accounts,cold_email_list,cold_email_campaign,cold_crm,linkedin \
  --module-params "cold_email_accounts.inbox_count=30,cold_email_list.prospects_count=2.500" \
  --monthly 1600 --setup 1600

node .claude/skills/sales-doc/script.mjs --mode invoice \
  --client "ACME Agency d.o.o." \
  --items "Mjesečne usluge:1000,Budžet Njemačka:1750,Budžet Hrvatska + Slovenija:300" \
  --bf 4933

node .claude/skills/sales-doc/script.mjs --mode contract \
  --client "NIKO d.o.o." --monthly 690 --setup 890 \
  --services "Google Ads i landing page" --rep-name "Goran Leutar" --rep-role direktor
```

## Output

Single Slack message in `#your-channel`:

```
*Nova ponuda — AGS Group d.o.o.*
Performance marketing | 1.000,00 EUR/mj + 1.000,00 EUR setup | budžet od 700 EUR/mj

Doc: https://docs.google.com/document/d/.../edit
Folder: Ponude 2026
```

Closer opens the Doc, fine-tunes, downloads as PDF, sends to prospect.

## Branded PDF offer (the Paradox-styled deliverable)

The Doc above is the **editable** offer. For a **send-ready, designed PDF in the Paradox house style** (dark cinematic + lime accent, Clash Display / General Sans, the your-domain.example wordmark — same look as the pitch brochure), use the branded-PDF engine. This is the standard polished offering a closer hands a prospect.

**Trigger this path when** the closer says any of: "ponuda u našem stilu / u našem PDF-u / u Paradox stilu", "nice PDF", "branded offer", "make it look like the ACME Agency one", or just "PDF" alongside an offer brief. It is also the right default whenever the offer is going **straight to the client** rather than needing manual fine-tuning. When in doubt, produce **both** — the editable Doc (normal flow) and the branded PDF.

**Engine:** `ACME Agency/scripts/build_offer_pdf.mjs` (renderer: `scripts/lib/offer_pdf.mjs`). It is deterministic: renders the PDF, self-verifies it exists and is a real file (`validateFiles`, ≥ 8 KB), and uploads to the client's Drive folder.

**Flow (what the skill does):**
1. Parse the closer's brief into a **spec object** (below). Reuse the SAME module ids as the Doc offer (`google_ads`, `linkedin`, `landing`, `crm`, `email`, `tracking`, …) so the PDF breakdown matches the Doc wording verbatim — the engine expands them from `service_modules.json`. Use explicit `services[]` only for bespoke bullets not in the catalog.
2. Write the spec to a JSON file (e.g. `ACME Agency/clients/ACME Agency/examples/<slug>.json` — see `ACME Agency-skaliranje.json` for a full worked example).
3. Run: `node ACME Agency/scripts/build_offer_pdf.mjs --spec <path.json>` (folder resolves from `spec.drive_folder_id`, or pass `--client "<name>"` to look it up in `clients.json`, or `--folder <id>`). Add `--no-upload` to pACME Agencyw locally first.
4. Return the Drive link. Croatian copy rules apply (no em-dashes, native phrasing, diacritics intact).

**Spec contract** (all fields optional except `client` + `pricing.monthly` + one of `services`/`modules`):

| Field | Meaning |
|---|---|
| `client` | "ACME Agency Group d.o.o." — header + footer + Drive lookup |
| `date` | "29.06.2026." (Croatian format, trailing dot) |
| `eyebrow` | small label above the title (e.g. "Nastavak saradnje"); default "Ponuda" |
| `title` / `title_accent` | cover headline; `title_accent` renders in lime on its own line |
| `intro` | 2-3 sentence cover paragraph (HTML `<b>` allowed) |
| `modules` | array of catalog ids → auto-expanded to service cards |
| `services` | array of `{ name, bullets:[], badge? }` for custom blocks (appended after modules) |
| `pricing` | `{ monthly, was?, setup?, setup_waived?, currency? }` — `was` shows the old price struck; `setup_waived:true` → "0 €" + "bez setup naknade"; `currency` EUR/BAM |
| `notes` | array of value-add / incentive / budget lines (the bordered boxes on the ponuda page; HTML `<b>` allowed) |
| `closing` | closing paragraph above the signature |
| `signer` | `{ name, role }` (default Faris Biogradlić / Head of Growth, ACME Agency) |
| `filename` | upload filename (no extension) |
| `drive_folder_id` | target Drive folder (else `--client` / `--folder`) |

**What the closer gives you →** what he sold (→ `modules`/`services`), the price (→ `pricing.monthly`), the incentive (a discount → `pricing.was` + a `notes` line; free add-on → a `badge` on that service + a `notes` line; waived setup → `setup_waived:true`), and optionally a date (→ `date`). Everything else has sane defaults.

**Example (a teammate's brief → spec → PDF):**

> "napravi ponudu za ACME Agency u našem stilu: google ads, smm, linkedin ads (gratis), optimizacija profila, 3 landinga, crm+tracking — 2000€/mj, bez setupa, prošla cijena 3000€"

→ build the spec (modules + the two LinkedIn blocks as `services` with a "Uključeno kao dodatna vrijednost" badge, `pricing:{monthly:2000, was:3000, setup_waived:true}`, a `notes` line for the free LinkedIn value) → `node ACME Agency/scripts/build_offer_pdf.mjs --spec ACME Agency.json` → branded PDF in ACME Agency's Drive folder. See [`clients/ACME Agency/examples/ACME Agency-skaliranje.json`](../../../ACME Agency/clients/ACME Agency/examples/ACME Agency-skaliranje.json).
