360 Secure SkillHub
360 安全技能中心

Tally Extractor

该 Skill 属于 Instance A 提取端:接收用户通过 Telegram/WhatsApp 发送的发票 PDF 或图片,利用视觉能力抽取公司、往来方、GSTIN、日期、行项目、税额与合计,做 GSTIN/HSN 格式、税额与合计平衡校验,生成规范化 JSON 并按 HMAC 签名 POST 到 Instance B 的桥接服务,再把结果用人话回复给用户;不直接写入 TallyPrime。

meetpaladiya44
meetpaladiya44
热度 25.04
最近更新 2026-05-18 21:12:48
分类:效率依赖:代码执行 / 第三方 API / 内部调用大模型应用场景:数据处理 / 财务 / 工作流

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add tally-extractor
Metadata
emoji
📄
name
tally-extractor
version
1.0.0
author
Maxxit
description
Instance A skill for TallyPrime (Extractor). Parses invoices/bills from PDF/image via Telegram/WhatsApp, extracts structured data (party, GSTIN, items, taxes, totals), and POSTs canonical JSON to the bridge service on Instance B. Does NOT post to Tally directly — that is handled by tally-skill on Instance B.
disableModelInvocation
false
requires
{"env":["BRIDGE_URL","BRIDGE_BEARER","BRIDGE_HMAC_SECRET"]}
metadata
{"openclaw":{"requiredEnv":["BRIDGE_URL","BRIDGE_BEARER","BRIDGE_HMAC_SECRET"],"bins":["curl"],"primaryCredential":"BRIDGE_URL"}}

Tally Extractor Skill

Extract invoice/bill data from PDFs and images received via Telegram or WhatsApp, convert to a canonical JSON payload, and POST to the bridge service on Instance B for Tally entry.

  • No direct Tally access: This skill does NOT connect to TallyPrime. Voucher posting is handled by tally-skill on Instance B.
  • Telegram/WhatsApp interface: User sends PDF/image → this skill extracts → bridge → Tally.
  • Structured output: Always emit the canonical JSON schema (see reference/extraction-schema.md).

Hero Use Case: Telegram invoice → Tally entry

Goal: zero manual entry for CAs handling many clients.

  1. User sends invoice PDF/image via Telegram.
  2. This skill extracts: company, party, GSTIN, date, invoice no, items (description, HSN, qty, rate, tax), total.
  3. Validate extraction: tax math check, GSTIN format, required fields present.
  4. POST canonical JSON to bridge (/v1/post-voucher).
  5. Receive result from bridge and reply to user in accountant-friendly language.

When to use this skill

Scope note: This skill is for Instance A (Extractor). It does not post to Tally — that responsibility belongs to tally-skill on Instance B.

Use when:

  • User sends a PDF or image of an invoice/bill via Telegram/WhatsApp
  • User requests to extract data from a document
  • User asks to "add this invoice to Tally" (extract + forward to bridge)
  • User provides invoice details as text message (parse and forward)

Do NOT use this skill for:

  • Direct Tally operations (reports, ledger queries) — those go to Instance B
  • PDF generation (tallyca runs on Instance B)
  • Master management (ledgers, groups) — that's on Instance B

Critical rules (must follow)

  1. Never hallucinate data: If a field is unclear or missing from the document, mark it as low-confidence or ask the user. Do not invent GSTINs, HSN codes, or amounts.
  2. Validate GSTIN format: 15 characters, pattern ^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$. If invalid, flag it.
  3. Validate HSN format: 4-8 digits. If invalid, flag it.
  4. Tax math check: taxable_amount × tax_rate / 100 ≈ tax_amount (within ±0.01). If mismatch, flag it.
  5. Total check: Sum of item amounts + taxes = total (within ±1 for rounding). If mismatch, flag it.
  6. Place of Supply: Derive from first 2 digits of party GSTIN (state code). Map to state name using reference/extraction-schema.md state code table.
  7. Inter-state vs Intra-state: If company GSTIN state ≠ party GSTIN state → IGST. If same state → CGST + SGST.
  8. Dates: Extract as YYYY-MM-DD. Common formats on invoices: DD/MM/YYYY, DD-MM-YYYY, DD.MM.YYYY, MMM DD, YYYY.
  9. Idempotency key: Generate deterministic GUID using pattern {companyShort}-{voucherType}-{invoiceNumber}-{date}.
  10. Confidence scores: Assign confidence (0.0-1.0) to each extracted field. Flag fields with confidence < 0.7 for user confirmation.

Extraction workflow

Step 1: Receive document

User sends PDF or image via Telegram. The document is available for OCR/vision processing.

Step 2: Check bridge health

Before extraction, verify Instance B is reachable:

curl -s -X GET \
  -H "Authorization: Bearer $BRIDGE_BEARER" \
  "$BRIDGE_URL/v1/health"

Expected response:

{
  "tally": "ok",
  "company_default": "ABC Company",
  "version": "1.0.0"
}

If tally: "down", inform user: "Tally is not running on the client machine. Please ask them to open TallyPrime."

Step 3: Extract data

Parse the document and extract:

Field Source Validation
Party name Invoice header Required
Party GSTIN Near party name or GST section 15-char format
Company GSTIN Near company name or letterhead 15-char format
Invoice number Invoice header Required
Invoice date Invoice header Parse to YYYY-MM-DD
Items Line items table At least one
Item description Line item Required per item
HSN/SAC Line item 4-8 digits
Quantity Line item Numeric
Unit Line item Optional
Rate Line item Numeric
Tax rate Line item or GST section Percentage
CGST/SGST/IGST amounts GST section Numeric
Total Invoice footer Required, must balance
Narration Notes section Optional

Step 4: Determine voucher type

Document type Voucher type
Purchase invoice (we are buyer) Purchase
Sales invoice (we are seller) Sales
Payment receipt Receipt
Payment voucher Payment
Credit note CreditNote
Debit note DebitNote

Clues:

  • "Tax Invoice" with company as seller → Sales
  • "Tax Invoice" with company as buyer → Purchase
  • "Receipt" or "Payment Received" → Receipt
  • "Credit Note" in header → CreditNote

If ambiguous, ask user: "Is this a purchase (you bought) or sales (you sold)?"

Step 5: Validate extraction

Run these checks before sending to bridge:

Check Rule Action if fails
Required fields All required fields present List missing fields, ask user
GSTIN format 15-char regex match Flag invalid, ask user
HSN format 4-8 digits Flag invalid, ask user
Tax math taxable × rate / 100 ≈ tax Flag mismatch, ask user
Total balance items + taxes = total (±1) Flag mismatch, ask user
Date parseable Valid date Ask user for correct date

Step 6: Build canonical JSON

Construct the JSON payload per reference/extraction-schema.md:

{
  "schema_version": "1.0",
  "request_id": "uuid-v4",
  "idempotency_key": "abc-purchase-xyz-186-20260518",
  "company": "ABC Company",
  "voucher": {
    "type": "Purchase",
    "date": "2026-05-18",
    "number": "186",
    "is_invoice_mode": true,
    "voucher_class": null,
    "narration": "Against Invoice 186",
    "party": {
      "name": "XYZ Party",
      "gstin": "27AABCU9603R1ZM",
      "place_of_supply": "Maharashtra",
      "registration_type": "Regular"
    },
    "company_gstin": "27AABCU9603R1ZN",
    "items": [...],
    "taxes": {...},
    "total": 46199.83,
    "bill_allocations": [...]
  },
  "source": {
    "kind": "pdf",
    "filename": "invoice_186.pdf",
    "extracted_at": "2026-05-18T10:30:00Z"
  },
  "confidence": {
    "overall": 0.93,
    "fields": {...}
  }
}

Step 7: POST to bridge

Send the JSON to Instance B:

# Compute HMAC
BODY='{"schema_version":"1.0",...}'
SIGNATURE=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$BRIDGE_HMAC_SECRET" | cut -d' ' -f2)

curl -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $BRIDGE_BEARER" \
  -H "X-Signature: hmac-sha256=$SIGNATURE" \
  -H "Idempotency-Key: abc-purchase-xyz-186-20260518" \
  -d "$BODY" \
  "$BRIDGE_URL/v1/post-voucher"

Full HTTP contract in reference/bridge.md.

Step 8: Handle response

Success

{
  "status": "posted",
  "guid": "abc-purchase-xyz-186-20260518",
  "voucher_number": "186",
  "company": "ABC Company",
  "summary": "Purchase voucher posted: XYZ Party, ₹46,199.83",
  "masters_created": ["XYZ Party"]
}

Reply to user (see reference/prompts.md):

Entry posted to Tally.

Company: ABC Company
Type: Purchase
Party: XYZ Party
Invoice No: 186
Date: 18 May 2026
Amount: ₹46,199.83 (Taxable: ₹39,152.40 + IGST: ₹7,047.43)

New ledger created: XYZ Party

Needs Clarification

{
  "status": "needs_clarification",
  "missing_fields": ["voucher.voucher_class"],
  "message": "Please confirm the voucher class name (e.g., 'Purchase @ 18 %')."
}

Forward the question to the user.

Error

{
  "status": "error",
  "error_code": "TALLY_UNREACHABLE",
  "message": "Could not connect to Tally."
}

Reply to user: "Tally is not responding. Please check that TallyPrime is open and try again."

Company name handling

The company name must match exactly in TallyPrime. Strategies:

  1. Explicit in document: Extract from invoice letterhead
  2. User specified: User may say "Add to ABC Company"
  3. Default from bridge: /v1/health returns company_default
  4. Ask user: If unclear, ask "Which Tally company should this entry go to?"

Voucher class handling

Some Tally companies use voucher classes for automatic GST splitting. This skill does NOT know which class to use — that's Instance B's job.

  • If user specifies class in message (e.g., "use Sales @ 18 %"), include in JSON
  • If not specified, leave voucher_class: null and let Instance B handle it
  • If Instance B returns needs_clarification for class, forward to user

Confidence scoring

Assign confidence scores based on:

Extraction quality Confidence
Clear text, high contrast, exact match 0.95 - 1.0
Readable but some ambiguity 0.7 - 0.94
Blurry, low contrast, guessed 0.4 - 0.69
Highly uncertain 0.0 - 0.39

Fields with confidence < 0.7 should be flagged for user confirmation before posting.

State code to state name mapping

First 2 digits of GSTIN → Place of Supply:

Code State
01 Jammu and Kashmir
02 Himachal Pradesh
03 Punjab
04 Chandigarh
05 Uttarakhand
06 Haryana
07 Delhi
08 Rajasthan
09 Uttar Pradesh
10 Bihar
11 Sikkim
12 Arunachal Pradesh
13 Nagaland
14 Manipur
15 Mizoram
16 Tripura
17 Meghalaya
18 Assam
19 West Bengal
20 Jharkhand
21 Odisha
22 Chhattisgarh
23 Madhya Pradesh
24 Gujarat
25 Daman and Diu
26 Dadra and Nagar Haveli
27 Maharashtra
28 Andhra Pradesh (Old)
29 Karnataka
30 Goa
31 Lakshadweep
32 Kerala
33 Tamil Nadu
34 Puducherry
35 Andaman and Nicobar Islands
36 Telangana
37 Andhra Pradesh
38 Ladakh

Deployment topology

There is exactly one chat interface (Telegram on Instance A). Instance B has no Telegram/WhatsApp — only the bridge HTTP endpoint.

flowchart LR
  User["CA / client on Telegram"] -->|"PDF / image"| TgBot["Telegram bot (Instance A)"]
  subgraph A["Instance A on dev EC2"]
    TgBot --> OpenClawA["OpenClaw\nLLM: Codex Plus session"]
    OpenClawA --> ExtractorSkill["tally-extractor-skill"]
  end
  OpenClawA -->|"HTTPS POST /v1/post-voucher"| Bridge["bridge-service\n(client box)"]
  subgraph B["Instance B on client CPU"]
    Bridge --> OpenClawB["OpenClaw\nLLM: OpenAI API key"]
    OpenClawB --> TallySkill["tally-skill"]
    TallySkill --> Tally["TallyPrime\nlocalhost:9000"]
  end
  Bridge --> OpenClawA
  OpenClawA --> User
Environment Instance A Instance B Tally access
Production Your EC2 (Telegram, Codex Plus) Client mini-PC beside Tally B → http://localhost:9000 (no ngrok for Tally)
Dev / testing Same Ubuntu EC2 Second OpenClaw on same EC2 B uses ngrok URL to remote Tally

Instance A reaches B via an inbound tunnel on the client box (ngrok http 8787 or Cloudflare Tunnel). Share BRIDGE_URL, BRIDGE_BEARER, and BRIDGE_HMAC_SECRET with the team running A.

Instance A configuration checklist

Step Setting Value
1 Host Ubuntu EC2 (or dev server)
2 Install Node.js, OpenClaw
3 LLM OpenAI ChatGPT Codex Plus session (subscription)
4 Skill loaded tally-extractor-skill/ only — do not load tally-skill/
5 Channel Telegram bot token on A only
6 BRIDGE_URL https://<client-tunnel>.ngrok.app (no trailing slash)
7 BRIDGE_BEARER Shared secret from client team
8 BRIDGE_HMAC_SECRET Shared HMAC secret from client team
9 Do not set TALLY_URL on Instance A
10 Preflight curl -H "Authorization: Bearer $BRIDGE_BEARER" $BRIDGE_URL/v1/health → tally: ok

Reference files

  • Extraction schema: reference/extraction-schema.md — Full JSON schema, examples
  • Bridge HTTP contract: reference/bridge.md — Endpoints, auth, errors
  • Reply templates: reference/prompts.md — User-facing messages