odoo-ps/odoo-feeder · Archived

odoo-demo-csv

Populate a connected Odoo demo database with data researched from a real company's website, or from a named industry. Use whenever a sales demo needs a database filled with realistic records.

Hot #1740 First seen Jul 1, 2026

Installation

$ npx skills add odoo-ps/odoo-feeder --skill odoo-demo-csv

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Default branch main
Open issues 0
Status Archived

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 10,329 B
  • docs SUMMARY.md 212 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 65 installs

SKILL.md

Odoo demo data

A sales person names a company and its website — or just an industry — plus a dataset size. You research the target, write Odoo-import-ready CSVs, import them into the connected database, and verify the result: one continuous pass ending with a SUMMARY: line.

Two words govern every step below.

Ground truth — every record traces back to something you found on the target site: real product names, real prices, real photos. Where the site is silent, stay silent — price 0.00, no image, and one line saying the data was thin. A demo built on invented products is the one failure this run cannot recover from.

Narrate — one line before each step saying what you are about to do, one line after each odoo-crud call carrying its result ("Imported 25 partners", "Preview found 1 bad column, fixing"). The sales person watches this output and sees nothing else of the run.

No real people — every customer, vendor and lead you invent is a fictional person or company; ground truth only covers the target company itself (its real products, prices, photos), never its staff's or its customers' contact details. Never let an invented contact resemble a real one, and never let its email or phone hint at any real company — including the target company itself: emails go on a domain that cannot resolve and carries no company name at all (example.com / example.org / example.net only — not @<company>-demo.test or any variant that embeds a real name), phone numbers use a range reserved for fiction (e.g. +1 555 01000199, or a country's official drama/fake-number block) — never a real subscriber range. This applies to every email/phone/mobile column you write, in every model, with no exceptions — including rows for the target company's own res.company or res.partner record.

The tool

odoo-crud <command> [options] reaches Odoo over XML-RPC. It is the only shell command available to you, and it reads its credentials from the environment.

  • Blocking — every call, install-modules included, returns once the work is

done. Chain straight into the next command: there is nothing to wait for and nothing to poll.

  • Every call prints JSON {"ok": true|false, ...}. Read ok before moving on.
  • odoo-crud <command> --help is the flag reference: the exact JSON shape of

--domain, --values, --args, --context, --filter lives there. Read it rather than guess a shape.

Use your own file tools to write the CSVs and your browsing tools to research. You have no shell beyond odoo-crud itself — writing CSV into the chat, or into a script you then try to run to generate the file, both go nowhere (the run to generate it is blocked); write the CSV content directly with your file tool.

When a call fails, a record you know exists comes back missing, or a number reads zero after a green import, the cause is almost always one of the Odoo behaviours collected in [ODOO-TRAPS.md](ODOO-TRAPS.md) — read it then.

Run

1. Research the target

Browse the site and collect: the sector, the real products or services and their prices, the company's location (it drives country and currency), and the URLs of the logo and the product photos. Make each URL absolute — some sites emit protocol-relative //cdn.example.com/…, which needs an https: prefix.

With no website given, generate coherent generic data for the stated industry instead.

Done when you can name the products you will import and the country you will set on the company.

2. Probe the database

This database's version decides which models, fields and apps exist — read them, never assume them.

  • odoo-crud auth-check — first command of the run. If it fails, stop and say

why; nothing downstream can work.

  • odoo-crud models --filter crm — which models are there.
  • odoo-crud fields product.template --filter 'name,list_price,barcode' — one

compact line per field (many2one required -> res.partner), including a selection field's allowed values. Filter by the columns you plan to write; dumping every field of res.partner or product.template buries the answer.

  • odoo-crud install-modules crm stock sale_management account — every module

behind step 5's import order, in ONE call. Read the list straight off that order: res.partnercontacts, product.templateproduct and salemanagement, stock on hand → stock, crm.leadcrm, plus account for invoicing. It installs and confirms before returning, and its JSON lists notinstalled for anything that failed.

Done when install-modules has come back with not_installed empty, and every column of every CSV you are about to write has appeared in a fields output, with its type and — for selection fields — its allowed values copied verbatim.

3. Configure the company

Set country, company name and currency on res.company to the real location: find the id with search-read, then

odoo-crud write res.company --ids '[1]' --values '{"country_id": 241, "currency_id": 23}'

A currency that seems absent is archived, not missing — see the traps file.

4. Size the dataset

The prompt gives small, medium or big. It is the scale of the whole business, so it applies to every model you populate. Anchor on the customer count and derive the rest in sector-adjusted proportion:

size customers (anchor) feel
small ~8 a small shop — just enough to demo
medium ~25 an established SMB
big ~80 a large company, rich data
  • vendors ≈ customers ÷ 6 — always a handful.
  • products ≈ a catalogue smaller than the customer base, fewer still for a

services business. When the site has fewer genuine products than that, reach the number with real variants (sizes, flavours, formats) — ground truth outranks the target count.

  • stock on hand — a quantity for every storable product.
  • leads ≈ half to one× the customers.
  • anything else you create — sale orders, invoices, BoMs — scaled to match.

Sizing one model richly and leaving its neighbours empty is what makes a demo look fake.

5. Generate and import

Write each CSV to a file, then odoo-crud import-csv <model> --file <path>. Import in dependency order so every reference resolves:

  1. res.partner — customers + vendors.

Columns: id (e.g. partnerclient1), name, is_company, street, city, email, phone.

  1. product.template — the flagship products you found.

Columns: id (e.g. product1), name, listprice, standard_price, barcode, plus this database's product-type and storability columns — whatever step 2's fields product.template --filter 'type,storable' showed, with the selection values verbatim. Whether storability is one of those values or its own boolean flag varies by version.

  1. stock on hand — a second pass over product.template, one row per

product you marked storable, carrying just id (the same external id as above) and qty_available. The ids already exist, so Odoo runs the same code path as typing a quantity on the product form: it creates the quant and applies it, and the goods really are on hand.

  1. crm.lead — columns: id (e.g. lead1), name, partnerid/id

(an id from file 1), expectedrevenue, description, stageid (New, Qualified or Proposition). Write every description in the customer's voice — the two or three lines that prospect sent in, naming the real product they are asking about and what they need it for.

Three rules bind every CSV:

  • External IDs — every row carries an id column and every reference is

field_id/id pointing at one. This is what makes a re-import update the record instead of duplicating it, and it is non-negotiable.

  • Quoting — comma separator, and every text value wrapped in double quotes

so "Company, Inc." stays one column.

  • Language — the site's language, or the one the sales person asked for.

Asked for more than the default set (mrp.bom, employees, chart of accounts…)? Build those the same way: sized to the anchor, wired with external IDs, imported after whatever they reference.

Done when every import has returned ok: true and its record count matches the number step 4 called for. On a failure, ODOO-TRAPS.md names the cause; fix the CSV and re-import — external ids make the retry safe.

6. Set images

A product grid with real photos is what makes the demo land on screen.

odoo-crud set-image product.template --id <record_id> --url <image_url>
odoo-crud set-image res.company --id 1 --url <logo_url> --field logo

Two details decide whether these calls land:

  • Database id, not external id. --id takes the integer Odoo assigned. Get

them with one search-read on the model you just imported — it returns each record's id beside its name, which is what you match your photo URLs on.

  • The field. It writes image_1920 by default; the company logo lives on

logo, so that call carries --field logo.

Records with no genuine image keep none — that is the only reason to skip one.

Done when every product whose photo URL you collected in step 1 has had a set-image call return ok: true, and the logo is on the company.

7. Verify, then summarise

Read back what you wrote — a green import is not proof:

  • odoo-crud search-read product.template --fields '["name","qty_available"]'

every storable product shows the quantity you imported. qtyavailable is the number that counts; inventoryquantity is not.

  • A search-read per model you populated, confirming the counts from step 4.

Done when every model you touched has been read back and matches. Then print the SUMMARY: line.