name: odoo-minimax-xlsx-o-spreadsheets description: "Open, create, read, analyze, edit, or validate spreadsheets in Odoo 19 format (.osps json) and XLSX format (.xlsx, .xlsm, .csv). Use when user asks to integrate with Odoo Documents, Dashboards, or Pivot tables. Supports dual generation: Native Odoo o-spreadsheet JSON (with PIVOT formulas) and strict-compatibility XLSX for Odoo import. Triggers on 'odoo spreadsheet', 'o-spreadsheet', 'dashboard', 'sales report', '.osps', or any Odoo-specific tabular data request." license: MIT bridge: false on-demand: true risk_level: high destructive: true security_warning: > XLSX files from untrusted sources may contain XXE attacks or Zip Bombs. NEVER process .xlsx from external/untrusted sources without validation. Always run: python3 scripts/odoo_sheet_tool.py xlsx --action validate --file {input} max_file_size_mb: 10 requires_user_confirmation: true metadata: version: "2.0" category: odoo-productivity sources: - Odoo 19 o-spreadsheet documentation - ECMA-376 Office Open XML File Formats
MiniMax XLSX Skill
Handle request directly. Do NOT spawn sub-agents. Always write output file user requests.
Task Routing
| Task | Method | Guide |
|---|---|---|
| ODOO NATIVE — JSON for Dashboards | json_builder.py |
references/osps-json.md |
| ODOO IMPORT — Strict XLSX | formula_check.py + pack |
references/odoo-import.md |
| READ — analyze existing data | xlsx_reader.py + pandas |
references/read-analyze.md |
| CREATE — generic xlsx | XML template | references/create.md |
| EDIT — modify existing xlsx | XML unpack→edit→pack | references/edit.md |
| VALIDATE — Odoo compatibility | formula_check.py |
references/validate.md |
READ — Analyze data (read references/read-analyze.md first)
Start with xlsx_reader.py for structure discovery, then pandas for custom analysis. Never modify source file.
Formatting rule: When user specifies decimal places (e.g. "2 decimal places"), apply format to ALL numeric values — use f'{v:.2f}' on every number. Never output 12875 when 12875.00 required.
Aggregation rule: Always compute sums/means/counts directly from DataFrame column — e.g. df['Revenue'].sum(). Never re-derive column values before aggregation.
CREATE — XML template (read references/create.md + references/format.md)
Copy templates/minimal_xlsx/ → edit XML directly → pack with xlsx_pack.py. Every derived value MUST be Excel formula (<f>SUM(B2:B9)</f>), never hardcoded number. Apply font colors per format.md.
EDIT — XML direct-edit (read references/edit.md first)
CRITICAL — EDIT INTEGRITY RULES:
- NEVER create new
Workbook()for edit tasks. Always load original file. - Output MUST contain same sheets as input (same names, same data).
- Only modify specific cells task asks for — everything else must be untouched.
- After saving output.xlsx, verify it: open with
xlsx_reader.pyorpandasand confirm original sheet names and sample of original data present. If verification fails, wrote wrong file — fix before delivering.
Never use openpyxl round-trip on existing files (corrupts VBA, pivots, sparklines). Instead: unpack → use helper scripts → repack.
"Fill cells" / "Add formulas to existing cells" = EDIT task. If input file already exists and told to fill, update, or add formulas to specific cells, MUST use XML edit path. Never create new Workbook(). Example — fill B3 with cross-sheet SUM formula:
python3 SKILL_DIR/scripts/xlsx_unpack.py input.xlsx /tmp/xlsx_work/
# Find target sheet's XML via xl/workbook.xml → xl/_rels/workbook.xml.rels
# Then use Edit tool to add <f> inside target <c> element:
# <c r="B3"><f>SUM('Sales Data'!D2:D13)</f><v></v></c>
python3 SKILL_DIR/scripts/xlsx_pack.py /tmp/xlsx_work/ output.xlsx
Add column (formulas, numfmt, styles auto-copied from adjacent column):
python3 SKILL_DIR/scripts/xlsx_unpack.py input.xlsx /tmp/xlsx_work/
python3 SKILL_DIR/scripts/xlsx_add_column.py /tmp/xlsx_work/ --col G \
--sheet "Sheet1" --header "% of Total" \
--formula '=F{row}/$F$10' --formula-rows 2:9 \
--total-row 10 --total-formula '=SUM(G2:G9)' --numfmt '0.0%' \
--border-row 10 --border-style medium
python3 SKILL_DIR/scripts/xlsx_pack.py /tmp/xlsx_work/ output.xlsx
--border-row flag applies top border to ALL cells in row (not just new column). Use when task requires accounting-style borders on total rows.
Insert row (shifts existing rows, updates SUM formulas, fixes circular refs):
python3 SKILL_DIR/scripts/xlsx_unpack.py input.xlsx /tmp/xlsx_work/
# IMPORTANT: Find correct --at row by searching for label text
# in worksheet XML, NOT by row number from prompt.
# Prompt may say "row 5 (Office Rent)" but Office Rent might actually
# be at row 4. Always locate row by text label first.
python3 SKILL_DIR/scripts/xlsx_insert_row.py /tmp/xlsx_work/ --at 5 \
--sheet "Budget FY2025" --text A=Utilities \
--values B=3000 C=3000 D=3500 E=3500 \
--formula 'F=SUM(B{row}:E{row})' --copy-style-from 4
python3 SKILL_DIR/scripts/xlsx_pack.py /tmp/xlsx_work/ output.xlsx
Row lookup rule: When task says "after row N (Label)", always find row by searching for "Label" in worksheet XML (grep -n "Label" /tmp/xlsx_work/xl/worksheets/sheet*.xml or check sharedStrings.xml). Use actual row number + 1 for --at. Do NOT call xlsx_shift_rows.py separately — xlsx_insert_row.py calls it internally.
Apply row-wide borders (e.g. accounting line on TOTAL row):
After running helper scripts, apply borders to ALL cells in target row, not just newly added cells. In xl/styles.xml, append new <border> with desired style, then append new <xf> in <cellXfs> that clones each cell's existing <xf> but sets new borderId. Apply new style index to every <c> in row via s attribute:
<!-- In xl/styles.xml, append to <borders>: -->
<border>
<left/><right/><top style="medium"/><bottom/><diagonal/>
</border>
<!-- Then append to <cellXfs> an xf clone with new borderId for each existing style -->
Key rule: When task says "add border to row N", iterate over ALL cells A through last column, not just newly added cells.
Manual XML edit (for anything helper scripts don't cover):
python3 SKILL_DIR/scripts/xlsx_unpack.py input.xlsx /tmp/xlsx_work/
# ... edit XML with Edit tool ...
python3 SKILL_DIR/scripts/xlsx_pack.py /tmp/xlsx_work/ output.xlsx
FIX — Repair broken formulas (read references/fix.md first)
EDIT task. Unpack → fix broken <f> nodes → pack. Preserve all original sheets and data.
VALIDATE — Check formulas (read references/validate.md first)
Run formula_check.py for static validation. Use libreoffice_recalc.py for dynamic recalculation when available.
Financial Color Standard
| Cell Role | Font Color | Hex Code |
|---|---|---|
| Hard-coded input / assumption | Blue | 0000FF |
| Formula / computed result | Black | 000000 |
| Cross-sheet reference formula | Green | 00B050 |
ODOO NATIVE Workflow (read references/osps-json.md)
When creating data for Odoo Dashboards or Documents:
- Receive data context from Odoo MCP.
- Use
scripts/json_builder.py(Pydantic models) to generate.ospsJSON. - CRITICAL: Formulas must start with
=. - Push JSON directly via
odoo_create_spreadsheet.
ODOO IMPORT Workflow (read references/odoo-import.md)
When creating .xlsx for manual import or Chatter:
- Apply Strict Linter: Use
scripts/formula_check.pyto ensure Arial fonts and solid fills. - Clean all text from newlines.
- Validate charts (Pie → Doughnut for multiseries).
- Run audit:
python3 scripts/formula_check.py output.xlsx.
Key Rules
- Formula-First: Every calculated cell MUST use Excel formula or JSON
=content. - Dual Mode: Choose between Native JSON (Dashboards) and Strict XLSX (Imports).
- JSON Syntax: Formulas in JSON use
=, XML<f>skips it. - Style Integrity: Maintain flat cell structures and global style dictionaries in JSON.
- Validation: Always run compatibility audit before delivery.
Commands
# Environment Setup: Recommended to use 'uv' for dependency management
uv run python3 scripts/json_builder.py --minimal
# Alternatively, install requirements manually
pip install -r requirements.txt
# Run auditor before delivery
python3 scripts/formula_check.py output.xlsx --report
Odoo Research Priority [MANDATORY]
All research query flows MUST respect Local-First Fallback Chain:
- Engram:
mem_search("odoo ${ODOO_VERSION} <topic>") - rg in Local Workspace (
${ODOO_COMMUNITY}/addons/, etc.) - Context7 MCP:
context7.resolve_library_id("odoo") - researcher agent:
scope_hint="docs",max_depth="standard" - Web Search (Google/GitHub): ONLY if all local sources exhausted or fail.
Pre-Flight Safety [MANDATORY before any xlsx operation]
# 1. Validate file is genuine XLSX
python3 scripts/odoo_sheet_tool.py xlsx --action validate --file "${INPUT}"
# 2. Size check (reject > 10MB)
FILE_SIZE=$(stat -c%s "${INPUT}" 2>/dev/null || stat -f%z "${INPUT}" 2>/dev/null || echo 0)
[ "${FILE_SIZE}" -gt 10485760 ] && {
echo "BLOCKED: file > 10MB — potential Zip Bomb" >&2; exit 1
}
# 3. Decompressed size check
python3 -c "
import zipfile, sys
with zipfile.ZipFile(sys.argv[1]) as z:
total = sum(i.file_size for i in z.infolist())
if total > 100 * 1024 * 1024:
print('BLOCKED: decompressed > 100MB')
sys.exit(1)
" "${INPUT}" || exit 1
Recovery Strategies [MANDATORY in every Odoo SKILL.md]
- xlsx_unpack fails → file corrupt; do NOT attempt repair; report to user
- sharedStrings.xml corrupt → HALT; restore from git; do not write back
- Any unhandled exception → preserve original; report full traceback