# Contract Portfolio Audit (合同梳理/合规审查)

Different from individual contract review (docx track changes). This is batch reading of an existing contract collection to produce a compliance overview for the client.

## When to use
- Client wants to understand the status and risks of their existing contract portfolio
- Goal is compliance oversight, not redlining individual contracts
- Deliverables: annotated PDFs + Excel summary table

## Workflow

### 1. File inventory
- Map the full directory structure and count files
- Group by logical categories (the client's folder structure usually reflects this)
- Record in MemPalace as a long-term project if ongoing

### 2. OCR scanned PDFs
- Most contracts from clients are scanned PDFs (no text layer)
- Check with `page.get_text().strip()` — empty = scanned
- Use `deepseek-ocr` skill (page-by-page image fallback if API unstable)
- See `deepseek-ocr` skill for retry strategy
- For large batches: convert all PDFs to images first, then OCR in a single loop with `time.sleep(1-2)` between pages

### 3. Analyze and annotate PDFs
- Add sticky note annotations via pymupdf (see `ocr-and-documents` skill)
- Author field: use `annot.set_info(title="WB")` + `annot.update()`
- Annotation content: risk points, missing clauses, suggestions — no risk level tags (【高风险】etc.)
- Suggestions must be specific and actionable ("建议增加……""建议修改为……"), not vague descriptions

### 4. Excel summary table
- Use openpyxl with proper styling (微软雅黑, header fill, borders, wrap_text, freeze panes)

#### File naming for deliverables (differs from standard versioned naming):
```
批注PDF:   客户名称-文件名称-批注-YYYYMMDD.pdf
汇总表:     客户名称-XX合同汇总表-YYYYMMDD.xlsx
```
Example:
```
南通新东方-BOSS直聘服务合同-批注-20260609.pdf
南通新东方-人力资源合同汇总表-20260609.xlsx
南通新东方-租赁合同汇总表-20260609.xlsx
```

#### Standard fields (adjustable per project):
| Field | Notes |
|-------|-------|
| 序号 | Sequential |
| 合同名称 | |
| 合同编号 | "无" if not present |
| 合同类型 | 采购服务/采购货物/租赁/etc. |
| 甲方（我方主体） | May have multiple entities |
| 乙方（对方主体） | |
| 合同期限 | Start-end dates |
| 合同金额 | With currency |
| 服务内容 | Brief description |
| 合同状态 | 履行中/即将到期/已到期 |
| 主要风险点 | Numbered list |
| 建议 | Numbered list, actionable |

### 5. Grouping logic (confirmed with Maggie for 南通新东方)
- **房租物业合同**: Group by 校区/租赁物 — rental + property management together in one sheet per campus
- **非模板其他合同**: Group by 合同类型 (采购货物/采购服务/租赁/etc.)
- **人力资源**: Flat list

### 6. Excel structure for rental contract portfolios

**Multi-sheet workbook**: one Excel file for all campuses.

| Sheet | Content |
|-------|---------|
| **总览** | One row per campus: campus name, tenant entity, landlord, current area, term, quarterly rent, quarterly property fee, deposit total, key risks, notes |
| **校区A** | All contracts for that campus in detail |
| **校区B** | ... |
| ... | ... |

**Per-campus sheet structure** (sections with merged header rows):

```
一、原租赁合同系列（乙方：XXX）
   [header row]
   主合同 → 补充协议 → 退租 → 三方转让 → ...

二、扩租合同系列（乙方：YYY）  [if applicable]
   [header row]
   主合同 → 补充协议 → ...

三、物业管理合同系列
   [header row]
   原租物业 → 补充协议 → 退租 → 扩租物业 → ...

四、校区整体风险分析与建议
   [merged A:K cell — 【整体风险分析】+ numbered risks + 【建议】]
   [merged A:K cell — 【文件汇总说明】+ source file ↔ table entry mapping]
```

**Section 四 has TWO merged cells** (both A:K, wrap_text=True, vertical=top):
1. **风险分析**: overall risks + recommendations (existing)
2. **文件汇总说明**: client-facing reconciliation — lists every source file by original directory, maps each to its table entry number, and explains any discrepancies (duplicates removed, entries combined, files reclassified to different sections). This lets the client cross-reference their original files against the table without guessing.

**Per-campus detail columns**:
序号, 文件名称, 合同类型, 合同当事人, 租赁标的/服务范围, 面积(㎡), 合同期限, 金额/费用, 核心内容, 当前状态, 风险点/备注

**Key points**:
- Each section has its own header row (repeated column headers after each section divider)
- Section dividers are merged cells with blue background
- Within each section, documents are listed in logical/chronological order (main contract → supplements → amendments)
- The risk analysis section at the bottom is a single large merged cell covering all columns
- Use freeze_panes on the title area so data scrolls while title stays

### 7. File count reconciliation (MANDATORY before upload)

After completing all sheets, reconcile source files against table entries:

1. **Count source files**: list all PDFs in the source directory tree
2. **Subtract known duplicates**: identical content files (same size + same date often = duplicate scan)
3. **Count table entries**: sum all numbered rows across the sheet
4. **Reconcile**: source_files - duplicates MUST equal table_entries. If not, identify missing files.

**Common miss patterns**:
- Two files with similar names/content silently merged into one row (e.g., `扩租物业补充协议.pdf` and `扩租物业补充协议（电费教育科技）1.pdf` — same theme but different signing parties, should be separate entries or explicitly noted as combined)
- A file in one subdirectory overlooked because a "similar" file in another subdirectory was already covered (e.g., `物业补充协议（电费培训学校）.pdf` missed because `物业补充xiey.pdf` seemed to cover the same topic)
- Files across subdirectories not cross-checked against the master list

**Rule**: Every unique file gets its own row unless explicitly combined (in which case the combined row must list all file names and the备注 must explain why they're grouped). One-to-one is the default; combining requires justification.

**Client-facing summary**: After reconciliation, add a 【文件汇总说明】section to the Excel (inside section 四, as a separate merged A:K cell below the risk analysis). Format:
```
【文件汇总说明】

客户提供XX校区相关合同文件共N份，分布在M个文件夹中。经核对整理，去重后为X份独立文件，汇总表中归纳为Y条记录。具体对照如下：

一、文件夹A/（N份）
  1. 文件名.pdf → 表第X项
  2. 文件名.pdf → 表第Y项
  ...

二、文件夹B/（N份，含M份重复）
  ...

说明：
  - 重复文件：XXX同时出现在A/和B/目录中，内容一致，仅计1份
  - 合并记录：XXX两份内容一致，合并为表第N项
  - 分类调整：A/目录下的XXX实为YYY，已归入ZZZ系列
```
This is NOT optional — every campus/category sheet must include this summary so the client can verify completeness without asking.

### 8. Upload to Nextcloud
- Place annotated PDFs alongside originals in same directory
- Place Excel in the parent category folder (e.g., 房租物业合同/ for the rental summary)
- `docker cp` → `chown www-data` → `php occ files:scan`

### 9. Sample-first validation
**Always start with a small sample** (2-3 contracts of different types):
1. Pick contracts that represent different complexity levels
2. Do full OCR → analyze → annotate → fill Excel
3. Send to Maggie for review of format and content
4. Adjust table structure based on feedback
5. Then batch process remaining contracts

This avoids rework — the table structure almost always needs adjustment after the first review.

## Key differences from individual contract review
| Aspect | Individual review | Portfolio audit |
|--------|------------------|-----------------|
| Input | Usually docx | Usually scanned PDF |
| Output | Track changes docx | Annotated PDF + Excel summary |
| Depth | Clause-by-clause redline | Risk overview and key terms extraction |
| Audience | Lawyer (for negotiation) | Client management (for compliance) |
| Volume | 1 contract at a time | Batch (tens to hundreds) |
| Workflow | uwf reviewer→editor | OCR → analyze → annotate → summarize |
| File naming | 当事人+文件+版本+修改人+日期 | 客户名+文件名+批注/汇总表+日期 |

## Related references
- `references/template-comparison-methodology.md` — when client has a standard template, how to produce the "与标准模版差异" column

## Pitfalls
- **Section 四 formatting**: The risk analysis section must use properly merged A:K cells. Common mistakes: (1) row heights set wrong (section headers should be ~30, not 80/350); (2) content cell not merged across all columns so text only shows in column A; (3) empty separator rows getting large fixed heights instead of auto. After building section 四, verify: `ws.merged_cells.ranges` includes both A:K rows, row heights are sane (None or ~30 for headers), and wrap_text=True on content cells.
- **CRITICAL: OnlyOffice does NOT reliably auto-size row heights — for merged cells OR regular cells with wrap_text.** Setting `height=None` (auto) on ANY cell with multi-line content means text WILL be cut off. This is the #1 user complaint ("文字显示不出来"). **Always set explicit heights on EVERY row with multi-line content**:
  1. After populating all cells, run a height estimation pass over ALL data rows
  2. For each row, find the cell with the most visual lines (accounting for CJK chars at ~2 width units each, line wrapping at `col_width * 1.2` chars)
  3. Set height = max_visual_lines × 15pt + 30-50% buffer
  4. For merged cells spanning A:K (~231 width), one text line ≈ one visual line (no wrapping)
  5. For narrow columns (H=20, K/L=40), long CJK lines wrap heavily — a 60-char Chinese line in a width-20 column = ~4 visual lines
  6. Write a reusable height-check script rather than eyeballing — the number of rows that need fixing always exceeds expectations
- **File count reconciliation**: after completing a campus/category sheet, always count source files and compare against table rows BEFORE uploading. The user should never be the one to discover a missing file. See step 7 above.
- Start with a small sample (2-3 contracts) to validate table headers with Maggie before batch processing
- Chinese text in Python strings with nested quotes (especially inside f-strings or dicts): use single quotes inside double or vice versa, or put long strings in variables
- openpyxl SyntaxError: Chinese punctuation like 、（）【】 inside nested Python string literals can confuse the parser — write the script to a .py file first, then run it, rather than using inline heredocs
- For complex campuses (like 北翼玖玖 with 17 files across 3 subdirs): use delegate_task to read all OCR'd files in parallel, then synthesize the analysis yourself
- When building a campus sheet, always trace the full document chain (main contract → what amended it → what superseded it) to understand the current state
- Watch for different legal entities signing different contracts at the same location (e.g., 培训学校 vs 教育科技) — flag this as a management complexity risk
- Watch for landlord entity changes mid-lease (common in commercial real estate) — trace the transfer chain and note deposit movements
