feat: upstream generic workspace tooling (board-sync, crm audit, catalog names)
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 5s
Tests / Test (Go 1.25) (pull_request) Successful in 1m16s
Tests / Test (Go stable) (pull_request) Successful in 1m36s
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 5s
Tests / Test (Go 1.25) (pull_request) Successful in 1m16s
Tests / Test (Go stable) (pull_request) Successful in 1m36s
Promote the generic, reusable parts of the private oo-workspace into the public library/CLI, so oo-workspace can shrink to business glue. - board.go: Board types + (c *Client) SyncBoard — upsert project milestones/tasks by exact title from a YAML board (dry-run when apply=false). CLI: oo projects board-sync. - crm_audit.go: OpportunityAudit + (c *Client) AuditOpportunities — file/task/member counts with a generic class (ok|dup|empty|junk-title). CLI: oo crm audit [--out]. - catalog/names.go: CleanPersonNames / GuessNameFromEmail / FormatProjectTitle (ported from oo-workspace). - catalog: adopt the newer apply/match logic — name repair (never encode company in lastName), company grouping key, contact-info type normalization, preserve an already-applied oo_id. Keeps the local Address/OOProjects fields and the config-driven classifier. Business-only oo-workspace commands (crm clean/migrate, folders, search) deliberately stay private.
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
var crmCmd = &cobra.Command{
|
||||
Use: "crm",
|
||||
Short: "CRM maintenance (dedupe, cleanup)",
|
||||
Short: "CRM maintenance (audit, dedupe, cleanup)",
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
crmCmd.AddCommand(crmAuditCmd())
|
||||
}
|
||||
|
||||
func crmAuditCmd() *cobra.Command {
|
||||
var outPath string
|
||||
cmd := &cobra.Command{
|
||||
Use: "audit",
|
||||
Short: "Audit opportunities (files/tasks/members per deal) and classify",
|
||||
Long: `Lists every opportunity with its file/task/member counts and a coarse class
|
||||
(ok | dup | empty | junk-title). --out writes the full JSON audit for later use.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
c, err := newOO(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
audits, err := c.AuditOpportunities(cmd.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if outPath != "" {
|
||||
b, merr := json.MarshalIndent(audits, "", " ")
|
||||
if merr != nil {
|
||||
return merr
|
||||
}
|
||||
if werr := os.WriteFile(outPath, b, 0o644); werr != nil {
|
||||
return werr
|
||||
}
|
||||
}
|
||||
byClass := map[string]int{}
|
||||
for _, a := range audits {
|
||||
byClass[a.Class]++
|
||||
}
|
||||
classes := make([]string, 0, len(byClass))
|
||||
for k := range byClass {
|
||||
classes = append(classes, k)
|
||||
}
|
||||
sort.Strings(classes)
|
||||
rows := make([]map[string]any, 0, len(classes))
|
||||
for _, k := range classes {
|
||||
rows = append(rows, map[string]any{"class": k, "count": byClass[k]})
|
||||
}
|
||||
printTable([]string{"class", "count"}, rows)
|
||||
if outPath != "" {
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "wrote %d audits → %s\n", len(audits), outPath)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&outPath, "out", "", "write audit JSON to this file")
|
||||
return cmd
|
||||
}
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
// Command tree is subject-based (mirrors the library split and the `tea` CLI):
|
||||
//
|
||||
// oo calendar list | events | add | delete
|
||||
// oo projects list | get | milestones | milestone-create | milestone-delete | create | update | delete | contacts (add|remove) | team (list|add|remove|set) | link-authors | link-git | files (list|upload|replace-in|update|download|rename|delete|dedupe|as-md|put-md|put-txt|put-xlsx)
|
||||
// oo projects list | get | milestones | milestone-create | milestone-delete | board-sync | create | update | delete | contacts (add|remove) | team (list|add|remove|set) | link-authors | link-git | files (list|upload|replace-in|update|download|rename|delete|dedupe|as-md|put-md|put-txt|put-xlsx)
|
||||
// oo tasks list | get | create | update | delete | subtask add | files (list|upload|detach)
|
||||
// oo users list | self | get | create | update | delete | block | unblock | password | check (alias: oo whoami)
|
||||
// oo link FILE_ID [FILE_ID...] DocEditor deep links (Products/Files/DocEditor.aspx?fileid=…)
|
||||
@@ -13,7 +13,7 @@
|
||||
// oo opportunities list | get | create | update | delete | stages | member-add | dedupe | dedupe-members | fix-titles
|
||||
// oo cases list | create | delete | member-add
|
||||
// oo crm-tasks list | create | delete | categories | reassign-self
|
||||
// oo crm cleanup
|
||||
// oo crm audit | cleanup
|
||||
// oo mails accounts | folders | list | get | download-attachment | draft | attach | draft-invoice | send | delete
|
||||
// oo invoices list | get | create | update | pdf | pdf-cleanup | status | delete | items …
|
||||
// oo docs tools | convert | pdf | presigned | csv | json | optimize | ocr | hocr | as-md | put-md | put-txt | put-xlsx
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
onlyoffice "github.com/eslider/go-onlyoffice"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
projectsCmd.AddCommand(prjBoardSyncCmd())
|
||||
}
|
||||
|
||||
// prjBoardSyncCmd upserts project milestones/tasks from a board YAML.
|
||||
func prjBoardSyncCmd() *cobra.Command {
|
||||
var apply bool
|
||||
cmd := &cobra.Command{
|
||||
Use: "board-sync BOARD.yaml",
|
||||
Short: "Upsert project milestones/tasks from a board YAML (dry-run by default)",
|
||||
Long: `Reads a board YAML (projects → milestones → tasks) and creates only the
|
||||
milestones/tasks that are missing, matching by exact title. Existing entries are
|
||||
left untouched, so the same file can be re-applied safely.
|
||||
|
||||
Dry-run by default; pass --apply to write to OnlyOffice.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
board, err := onlyoffice.LoadBoard(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c, err := newOO(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := c.SyncBoard(cmd.Context(), board, apply)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mode := "dry-run"
|
||||
if apply {
|
||||
mode = "apply"
|
||||
}
|
||||
printObject(map[string]any{
|
||||
"mode": mode,
|
||||
"projects": len(board.Projects),
|
||||
"created_milestones": res.CreatedMilestones,
|
||||
"skipped_milestones": res.SkippedMilestones,
|
||||
"created_tasks": res.CreatedTasks,
|
||||
"skipped_tasks": res.SkippedTasks,
|
||||
})
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().BoolVar(&apply, "apply", false, "write to OnlyOffice (default: dry-run)")
|
||||
return cmd
|
||||
}
|
||||
Reference in New Issue
Block a user