@bergetai/odoo-cli 0.6.1 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -57,6 +57,7 @@ odoo crm teams delete 5 --dry-run
57
57
  # Internal notes (NEVER sends email to customer)
58
58
  odoo helpdesk tickets add-note 398 --body "Internal note text"
59
59
  odoo helpdesk tickets add-note 398 --body-file note.md
60
+ odoo crm records add-note 338 --body "Customer replied: ..." --author 40
60
61
 
61
62
  # Log note correction (message-update / message-delete; comment-type only)
62
63
  odoo crm records message-update 270799 --body-file fixed.html --dry-run
@@ -80,7 +81,7 @@ Note: `odoo --version` prints the CLI's own version; `odoo version` queries the
80
81
  - Documents surface (`documents`) — `tree` (hierarchical folder listing with `--count`/`--depth`/`--folder`), `records list/show` (folders excluded by default, `--type folder|binary|url|all`, `--folder`/`--owner`/`--partner`/`--tag`), `folders list`, `records create` (folder via `--folder-type folder`, link via `--link <url>`), `records update` (move via `--folder`), `records delete`, and `upload` (local file → base64 `datas`, MIME guessed from extension, `--dry-run` previews without sending bytes). Folders are `documents.document` rows with `type='folder'`; the hierarchy is `folder_id`. Module-not-installed error (exit 4)
81
82
  - Writes (`create`, `update`, `delete`) — single-call atomic batch `write`, client-side no-op diffing, delete convergence with `skipped` ids, post-failure "record may have been created" hint on `create`
82
83
  - `--dry-run` for all mutations — full validation, resolution, and before/after diffs without mutating
83
- - Internal notes (`add-note`) — creates internal Log Notes only (subtype_id=2, message_type=comment), **NEVER sends email to customer**, by design prevents accidental customer communication
84
+ - Internal notes (`add-note`) — creates internal Log Notes only (subtype_id=2, message_type=comment), **NEVER sends email to customer**, by design prevents accidental customer communication. Optional `--author <email|name|id>` attributes the note to the partner who actually sent the logged message (resolved via `res.partner`; ambiguous matches list candidates and never guess)
84
85
  - Log note correction (`message-update`, `message-delete`) — repair formatting or remove duplicate log notes; strictly limited to comment-type messages (emails/notifications refused by design), full `--dry-run` support, unknown delete ids skipped
85
86
  - Config sources and precedence with atomic `0600` config writes, XML-RPC transport with timeouts and HTTPS enforcement, and an auth failure taxonomy (db missing / bad credentials / unreachable)
86
87
 
package/dist/cli.js CHANGED
@@ -139,6 +139,7 @@ Examples:
139
139
  .argument("<id>", "record id")
140
140
  .option("--body <text>", "note body (HTML allowed)")
141
141
  .option("--body-file <path>", "read note body from file ('-' for stdin)")
142
+ .option("--author <email|name|id>", "note author (res.partner; default: your own partner)")
142
143
  .addHelpText("after", `
143
144
  IMPORTANT: This command creates INTERNAL notes only. These notes:
144
145
  • Are NEVER sent to the customer via email
@@ -151,7 +152,8 @@ This is by design to prevent accidental customer emails.
151
152
  Examples:
152
153
  $ odoo crm records add-note 338 --body "Internal note text"
153
154
  $ odoo crm records add-note 338 --body-file note.md
154
- $ cat note.md | odoo crm records add-note 338 --body-file -`)
155
+ $ cat note.md | odoo crm records add-note 338 --body-file -
156
+ $ odoo crm records add-note 338 --body "Kundens svar: ..." --author andreas@berget.ai`)
155
157
  .action(async (id, opts) => {
156
158
  await run((g) => addNoteCommand(id, opts, g, "crm.lead"), program);
157
159
  });
@@ -391,6 +393,7 @@ the raw form.`)
391
393
  .argument("<id>", "ticket id")
392
394
  .option("--body <text>", "note body (HTML allowed)")
393
395
  .option("--body-file <path>", "read note body from file ('-' for stdin)")
396
+ .option("--author <email|name|id>", "note author (res.partner; default: your own partner)")
394
397
  .addHelpText("after", `
395
398
  IMPORTANT: This command creates INTERNAL notes only. These notes:
396
399
  • Are NEVER sent to the customer via email
@@ -403,7 +406,8 @@ This is by design to prevent accidental customer emails.
403
406
  Examples:
404
407
  $ odoo helpdesk tickets add-note 398 --body "Internal note text"
405
408
  $ odoo helpdesk tickets add-note 398 --body-file note.md
406
- $ cat note.md | odoo helpdesk tickets add-note 398 --body-file -`)
409
+ $ cat note.md | odoo helpdesk tickets add-note 398 --body-file -
410
+ $ odoo helpdesk tickets add-note 398 --body "Kundens svar: ..." --author kund@example.com`)
407
411
  .action(async (id, opts) => {
408
412
  await run((g) => addNoteCommand(id, opts, g, "helpdesk.ticket"), program);
409
413
  });
@@ -1,6 +1,7 @@
1
1
  import { OdooClient } from "../client.js";
2
2
  import { resolveConfig } from "../config.js";
3
3
  import { notFoundError, usageError } from "../errors.js";
4
+ import { resolveReference } from "../model.js";
4
5
  import { printJson, verbose as logVerbose } from "../output.js";
5
6
  import { formatTable } from "../table.js";
6
7
  const THREAD_MODELS = {
@@ -55,6 +56,21 @@ export async function addNoteCommand(recordId, opts, globals, model) {
55
56
  if (!rows || rows.length === 0) {
56
57
  throw notFoundError("record_not_found", `${spec.model} #${id} does not exist`, `check the ${spec.label} id`);
57
58
  }
59
+ // Optional: attribute the note to the person who actually sent the
60
+ // logged message (e.g. a forwarded customer email). author_id is a
61
+ // res.partner — resolve by exact email, name, or numeric id. Without
62
+ // --author Odoo defaults to the authenticated user's partner.
63
+ let author;
64
+ if (opts.author) {
65
+ author = await resolveReference(client, "res.partner", opts.author, {
66
+ label: "author",
67
+ searchFields: ["name"],
68
+ exactFields: ["email"],
69
+ });
70
+ if (globals.verbose) {
71
+ logVerbose(`resolved --author "${opts.author}" -> [${author.id}, "${author.name}"]`, globals.verbose);
72
+ }
73
+ }
58
74
  // Create internal Log Note
59
75
  // CRITICAL: These parameters ensure the note is INTERNAL ONLY
60
76
  // - subtype_id=2: "Note" subtype (internal, not sent to customer)
@@ -66,6 +82,9 @@ export async function addNoteCommand(recordId, opts, globals, model) {
66
82
  message_type: "comment",
67
83
  subtype_id: 2, // Note subtype = internal only, NEVER sends email
68
84
  };
85
+ if (author) {
86
+ noteParams.author_id = author.id;
87
+ }
69
88
  if (globals.verbose) {
70
89
  logVerbose(`Creating internal note for ${spec.model} #${id} (${rows[0].name})`, globals.verbose);
71
90
  logVerbose(`Note parameters: ${JSON.stringify(noteParams, null, 2)}`, globals.verbose);
@@ -78,6 +97,7 @@ export async function addNoteCommand(recordId, opts, globals, model) {
78
97
  record_name: rows[0].name,
79
98
  note_type: "internal_log_note",
80
99
  will_send_email: false,
100
+ author: author ? { id: author.id, name: author.name } : null,
81
101
  body_preview: body.substring(0, 100) + (body.length > 100 ? "..." : ""),
82
102
  body_length: body.length,
83
103
  };
@@ -88,6 +108,9 @@ export async function addNoteCommand(recordId, opts, globals, model) {
88
108
  console.log(`[DRY RUN] Would create internal Log Note:`);
89
109
  console.log(` Record: ${spec.model} #${id} — ${rows[0].name}`);
90
110
  console.log(` Type: Internal note (never sent to customer)`);
111
+ if (author) {
112
+ console.log(` Author: ${author.name} (${author.id})`);
113
+ }
91
114
  console.log(` Body length: ${body.length} characters`);
92
115
  console.log(` Body preview: ${result.body_preview}`);
93
116
  }
@@ -103,6 +126,7 @@ export async function addNoteCommand(recordId, opts, globals, model) {
103
126
  record_name: rows[0].name,
104
127
  note_type: "internal_log_note",
105
128
  email_sent: false,
129
+ author: author ? { id: author.id, name: author.name } : null,
106
130
  };
107
131
  if (globals.json) {
108
132
  printJson(result);
@@ -111,6 +135,9 @@ export async function addNoteCommand(recordId, opts, globals, model) {
111
135
  console.log(`✓ Internal Log Note created (ID: ${noteId})`);
112
136
  console.log(` Record: ${spec.model} #${id} — ${rows[0].name}`);
113
137
  console.log(` Type: Internal note (not sent to customer)`);
138
+ if (author) {
139
+ console.log(` Author: ${author.name} (${author.id})`);
140
+ }
114
141
  }
115
142
  }
116
143
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bergetai/odoo-cli",
3
- "version": "0.6.1",
3
+ "version": "0.7.1",
4
4
  "description": "Agent-first CLI for the Odoo External API",
5
5
  "publishConfig": {
6
6
  "access": "public"