package main import ( "fmt" "strconv" onlyoffice "github.com/eslider/go-onlyoffice" "github.com/spf13/cobra" ) var opportunitiesCmd = &cobra.Command{ Use: "opportunities", Aliases: []string{"deals", "deal"}, Short: "CRM opportunities (a.k.a. deals)", } func init() { rootCmd.AddCommand(opportunitiesCmd) opportunitiesCmd.AddCommand(oppListCmd()) opportunitiesCmd.AddCommand(oppGetCmd()) opportunitiesCmd.AddCommand(oppCreateCmd()) opportunitiesCmd.AddCommand(oppUpdateCmd()) opportunitiesCmd.AddCommand(oppDeleteCmd()) opportunitiesCmd.AddCommand(oppStagesCmd()) opportunitiesCmd.AddCommand(oppMemberAddCmd()) opportunitiesCmd.AddCommand(oppDedupeCmd()) opportunitiesCmd.AddCommand(oppDedupeMembersCmd()) opportunitiesCmd.AddCommand(oppFixTitlesCmd()) } func oppListCmd() *cobra.Command { var count, offset int cmd := &cobra.Command{ Use: "list", Short: "List opportunities", RunE: func(cmd *cobra.Command, args []string) error { c, err := newOO(cmd) if err != nil { return err } list, total, err := c.ListOpportunities(cmd.Context(), count, offset) if err != nil { return err } if outputFormat == "table" { fmt.Printf("total: %d (shown: %d)\n", total, len(list)) // Flatten nested bidCurrency {abbreviation, ...} for readability. for _, row := range list { if cur, ok := row["bidCurrency"].(map[string]any); ok { row["bidCurrency"] = cur["abbreviation"] } } } printTable([]string{"id", "title", "bidValue", "bidCurrency", "stageName"}, list) return nil }, } cmd.Flags().IntVar(&count, "count", 100, "") cmd.Flags().IntVar(&offset, "offset", 0, "") return cmd } func oppGetCmd() *cobra.Command { return &cobra.Command{ Use: "get OPPORTUNITY_ID", Short: "Show an opportunity by id", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { c, err := newOO(cmd) if err != nil { return err } out, err := c.GetOpportunity(cmd.Context(), args[0]) if err != nil { return err } printObject(out) return nil }, } } func oppCreateCmd() *cobra.Command { var stage int var bid float64 var currency, responsible, desc string var contacts []string cmd := &cobra.Command{ Use: "create TITLE", Aliases: []string{"add"}, Short: "Create an opportunity", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { c, err := newOO(cmd) if err != nil { return err } if stage == 0 { stage = 1 } out, err := c.CreateOpportunity(cmd.Context(), args[0], stage, responsible, currency, desc, bid) if err != nil { return err } did := strconv.Itoa(int(flexIDFloat(out["id"]))) for _, cid := range contacts { _, _ = c.AddOpportunityMember(cmd.Context(), did, cid) } printObject(out) return nil }, } cmd.Flags().IntVar(&stage, "stage", 1, "pipeline stage id") cmd.Flags().Float64Var(&bid, "bid", 0, "bid value") cmd.Flags().StringVar(¤cy, "currency", "EUR", "bid currency") cmd.Flags().StringVar(&responsible, "responsible", "", "responsible user id") cmd.Flags().StringVar(&desc, "description", "", "description") cmd.Flags().StringSliceVar(&contacts, "contact", nil, "contact id to add as member (repeatable)") return cmd } func oppUpdateCmd() *cobra.Command { var title, desc string var stage int64 var bid float64 cmd := &cobra.Command{ Use: "update OPPORTUNITY_ID", Short: "Update opportunity title, description, stage, or bid", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { c, err := newOO(cmd) if err != nil { return err } out, err := c.UpdateOpportunity(cmd.Context(), args[0], onlyoffice.UpdateOpportunityParams{ Title: title, Description: desc, StageID: stage, BidValue: bid, BidValueSet: cmd.Flags().Changed("bid"), }) if err != nil { return err } printObject(out) return nil }, } cmd.Flags().StringVar(&title, "title", "", "new title") cmd.Flags().StringVar(&desc, "description", "", "new description") cmd.Flags().Int64Var(&stage, "stage", 0, "pipeline stage id") cmd.Flags().Float64Var(&bid, "bid", 0, "bid value") return cmd } func oppDeleteCmd() *cobra.Command { return &cobra.Command{ Use: "delete OPPORTUNITY_ID [OPPORTUNITY_ID...]", Aliases: []string{"rm"}, Short: "Delete one or more opportunities", Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { c, err := newOO(cmd) if err != nil { return err } for _, id := range args { out, err := c.DeleteOpportunity(cmd.Context(), id) if err != nil { return err } printObject(out) } return nil }, } } func oppStagesCmd() *cobra.Command { return &cobra.Command{ Use: "stages", Short: "List pipeline stages", RunE: func(cmd *cobra.Command, args []string) error { c, err := newOO(cmd) if err != nil { return err } out, err := c.ListDealStages(cmd.Context()) if err != nil { return err } printTable([]string{"id", "title", "sortOrder", "color"}, out) return nil }, } } func oppMemberAddCmd() *cobra.Command { return &cobra.Command{ Use: "member-add OPPORTUNITY_ID CONTACT_ID", Short: "Attach a contact to an opportunity", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { c, err := newOO(cmd) if err != nil { return err } out, err := c.AddOpportunityMember(cmd.Context(), args[0], args[1]) if err != nil { return err } printObject(out) return nil }, } } func oppDedupeCmd() *cobra.Command { var ignoreCompanySuffix bool cmd := &cobra.Command{ Use: "dedupe", Short: "Merge duplicate opportunities by title", RunE: dedupeRunE(func(cmd *cobra.Command, c *onlyoffice.Client) error { res, err := onlyoffice.DedupeOpportunities(cmd.Context(), c, ignoreCompanySuffix) if err != nil { return err } printObject(res) return nil }), } cmd.Flags().BoolVar(&ignoreCompanySuffix, "ignore-company-suffix", false, "group by position only") return cmd } func oppDedupeMembersCmd() *cobra.Command { return &cobra.Command{ Use: "dedupe-members", Short: "Remove duplicate contacts on each deal (by id and display name)", RunE: dedupeRunE(func(cmd *cobra.Command, c *onlyoffice.Client) error { res, err := onlyoffice.DedupeOpportunityMembers(cmd.Context(), c) if err != nil { return err } printObject(res) return nil }), } } func oppFixTitlesCmd() *cobra.Command { return &cobra.Command{ Use: "fix-titles", Short: "Fix malformed deal titles (leading @, spacing)", RunE: dedupeRunE(func(cmd *cobra.Command, c *onlyoffice.Client) error { res, err := onlyoffice.FixOpportunityTitles(cmd.Context(), c) if err != nil { return err } printObject(res) return nil }), } }