v0.4.1: Add parent_id to create_project; link_drive_document_to_project; 4 Cybrosys checklist tools (97 total)
This commit is contained in:
+130
-2
@@ -714,16 +714,20 @@ def list_task_stages(project_id: int = None) -> list:
|
|||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def create_project(name: str, description: str = "", user_id: int = None,
|
def create_project(name: str, description: str = "", user_id: int = None,
|
||||||
date_start: str = "", date: str = "",
|
date_start: str = "", date: str = "",
|
||||||
privacy_visibility: str = "employees") -> int:
|
privacy_visibility: str = "employees",
|
||||||
|
parent_id: int = None) -> int:
|
||||||
"""Create a new project. Returns the new project ID.
|
"""Create a new project. Returns the new project ID.
|
||||||
|
parent_id: ID of the parent project.project — use to create sub-projects within a programme.
|
||||||
privacy_visibility: 'employees' (all employees), 'portal' (employees + invited portal users),
|
privacy_visibility: 'employees' (all employees), 'portal' (employees + invited portal users),
|
||||||
'followers' (invited internal users only).
|
'followers' (invited internal users only).
|
||||||
Trigger phrases: "create project", "new project", "add a project", "start a project"."""
|
Trigger phrases: "create project", "new project", "add a project", "start a project",
|
||||||
|
"create sub-project", "create child project"."""
|
||||||
vals: dict = {"name": name}
|
vals: dict = {"name": name}
|
||||||
if description: vals["description"] = description
|
if description: vals["description"] = description
|
||||||
if user_id: vals["user_id"] = user_id
|
if user_id: vals["user_id"] = user_id
|
||||||
if date_start: vals["date_start"] = date_start
|
if date_start: vals["date_start"] = date_start
|
||||||
if date: vals["date"] = date
|
if date: vals["date"] = date
|
||||||
|
if parent_id: vals["parent_id"] = parent_id
|
||||||
vals["privacy_visibility"] = privacy_visibility or "employees"
|
vals["privacy_visibility"] = privacy_visibility or "employees"
|
||||||
return _create("project.project", vals)
|
return _create("project.project", vals)
|
||||||
|
|
||||||
@@ -937,6 +941,130 @@ def update_task_stage(stage_id: int, name: str = "", sequence: int = None,
|
|||||||
return _write("project.task.type", stage_id, vals) if vals else False
|
return _write("project.task.type", stage_id, vals) if vals else False
|
||||||
|
|
||||||
|
|
||||||
|
# ── Link Drive document to project ───────────────────────────────────────────
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def link_drive_document_to_project(project_id: int, drive_url: str,
|
||||||
|
name: str = "Google Drive Folder") -> int:
|
||||||
|
"""Attach a Google Drive folder or document URL to a project.project record
|
||||||
|
as an ir.attachment of type 'url'. The link appears in the project's
|
||||||
|
Attachments panel in Odoo. Returns the new attachment ID.
|
||||||
|
Trigger phrases: "link drive folder to project", "attach drive to project",
|
||||||
|
"add drive link to project", "connect drive folder"."""
|
||||||
|
return _create("ir.attachment", {
|
||||||
|
"name": name,
|
||||||
|
"type": "url",
|
||||||
|
"url": drive_url,
|
||||||
|
"res_model": "project.project",
|
||||||
|
"res_id": project_id,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
# ── Task Checklists (projects_task_checklists — Cybrosys) ─────────────────────
|
||||||
|
# Models: task.checklist (templates), checklist.item (template items),
|
||||||
|
# checklist.item.line (per-task instances). State: todo/in_progress/done/cancel.
|
||||||
|
# project.task extended with: checklist_id (Many2one), checklists_ids (One2many via projects_id).
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def list_checklist_templates(query: str = "", limit: int = 30) -> list:
|
||||||
|
"""List available task.checklist templates with their items.
|
||||||
|
Trigger phrases: "checklist templates", "list checklists", "available checklists",
|
||||||
|
"show checklist templates"."""
|
||||||
|
domain = [["name", "ilike", query]] if query else []
|
||||||
|
templates = _search_read("task.checklist", domain,
|
||||||
|
["name", "description", "checklist_ids"], limit=limit)
|
||||||
|
for t in templates:
|
||||||
|
if t.get("checklist_ids"):
|
||||||
|
t["items"] = _search_read(
|
||||||
|
"checklist.item",
|
||||||
|
[["id", "in", t["checklist_ids"]]],
|
||||||
|
["name", "sequence", "description"],
|
||||||
|
limit=200,
|
||||||
|
)
|
||||||
|
return templates
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def get_task_checklists(task_id: int) -> list:
|
||||||
|
"""Get all checklist item lines (checklist.item.line) attached to a task,
|
||||||
|
with state and item name. State values: 'todo', 'in_progress', 'done', 'cancel'.
|
||||||
|
Task progress % is recomputed automatically from these states in Odoo.
|
||||||
|
Trigger phrases: "task checklist", "checklist items", "checklist status",
|
||||||
|
"acceptance criteria", "phase checklist", "what's checked off"."""
|
||||||
|
return _search_read(
|
||||||
|
"checklist.item.line",
|
||||||
|
[["projects_id", "=", task_id]],
|
||||||
|
["check_list_item_id", "description", "checklist_id", "state"],
|
||||||
|
limit=200,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def add_checklist_to_task(task_id: int, checklist_id: int) -> int:
|
||||||
|
"""Attach a task.checklist template to a task by creating checklist.item.line records.
|
||||||
|
Idempotent — items already added for this checklist+task combination are skipped.
|
||||||
|
Also sets checklist_id on the task to the given template.
|
||||||
|
Returns count of new lines created.
|
||||||
|
Use list_checklist_templates to find valid checklist_id values.
|
||||||
|
NOTE: Replicates the UI onchange since that only fires client-side.
|
||||||
|
Trigger phrases: "add checklist to task", "attach checklist", "apply checklist",
|
||||||
|
"add acceptance criteria", "apply phase checklist"."""
|
||||||
|
# Items already present for this checklist on this task
|
||||||
|
existing = _search_read(
|
||||||
|
"checklist.item.line",
|
||||||
|
[["projects_id", "=", task_id], ["checklist_id", "=", checklist_id]],
|
||||||
|
["check_list_item_id"],
|
||||||
|
limit=200,
|
||||||
|
)
|
||||||
|
existing_item_ids = {
|
||||||
|
r["check_list_item_id"][0]
|
||||||
|
for r in existing
|
||||||
|
if isinstance(r.get("check_list_item_id"), list)
|
||||||
|
}
|
||||||
|
# Template items not yet added
|
||||||
|
items = _search_read(
|
||||||
|
"checklist.item",
|
||||||
|
[["checklist_id", "=", checklist_id]],
|
||||||
|
["id"],
|
||||||
|
limit=200,
|
||||||
|
)
|
||||||
|
new_items = [it for it in items if it["id"] not in existing_item_ids]
|
||||||
|
if not new_items:
|
||||||
|
return 0
|
||||||
|
commands = [
|
||||||
|
(0, 0, {"check_list_item_id": it["id"], "checklist_id": checklist_id, "state": "todo"})
|
||||||
|
for it in new_items
|
||||||
|
]
|
||||||
|
_write("project.task", task_id, {
|
||||||
|
"checklist_id": checklist_id,
|
||||||
|
"checklists_ids": commands,
|
||||||
|
})
|
||||||
|
return len(new_items)
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def update_checklist_item(line_id: int, state: str) -> bool:
|
||||||
|
"""Update the state of a checklist.item.line.
|
||||||
|
state: 'todo' | 'in_progress' | 'done' | 'cancel'.
|
||||||
|
Uses Odoo action methods so task progress % and chatter are updated automatically.
|
||||||
|
Use get_task_checklists to find valid line_id values.
|
||||||
|
Trigger phrases: "mark checklist item done", "check off item", "mark in progress",
|
||||||
|
"cancel checklist item", "complete acceptance criteria", "mark phase complete"."""
|
||||||
|
if state == "in_progress":
|
||||||
|
_call("checklist.item.line", "action_approve_and_next", [[line_id]], {})
|
||||||
|
elif state == "done":
|
||||||
|
_call("checklist.item.line", "action_mark_completed", [[line_id]], {})
|
||||||
|
elif state == "cancel":
|
||||||
|
_call("checklist.item.line", "action_mark_canceled", [[line_id]], {})
|
||||||
|
elif state == "todo":
|
||||||
|
_write("checklist.item.line", line_id, {"state": "todo"})
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid state '{state}'. Valid values: 'todo', 'in_progress', 'done', 'cancel'."
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
# ════════════════════════════════════════════════════════════════════════════
|
# ════════════════════════════════════════════════════════════════════════════
|
||||||
# HELPDESK
|
# HELPDESK
|
||||||
# ════════════════════════════════════════════════════════════════════════════
|
# ════════════════════════════════════════════════════════════════════════════
|
||||||
|
|||||||
Reference in New Issue
Block a user