Optional params and vararg params in a single formula

I encountered a problem when creating a formula where I wanted to use both optional parameters and vararg parameters. The use case is:

  • Formula should add (create) an invoice in an external invoicing system.
  • Some params should be required (e.g. clientId).
  • Some params can be optional and set to default values if omitted (e.g. issueDate which is set to today by default).
  • The invoice can include multiple items and each item has several subfields (e.g. name, quantity, price etc.). Those items should be also included as params in the formula.

The last requirement turned out to be the most problematic. The solutions I considered:

  • Ideally, invoice items would be provided as objects but formulas don’t allow object params.
  • So, I thought of having a set of array params, one for each invoice item field (e.g. list of names, list of quantities, list of prices etc.) but this doesn’t seem to be intuitive and good information architecture.
  • Finally, I decided to use several vararg params, one for each invoice field item.

The problem is, Coda requires me to provide all optional params when I want to call the formula with invoice items (vararg params) and skip optional params. Otherwise, it assumes the vararg params represent optional params and shows “Incorrect named argument” error:

The formula code looks the following (I skipped some params to show the core concept):

pack.addFormula({
  name: "AddInvoice",
  description: "Add a new invoice.",
  isAction: true,

  resultType: coda.ValueType.Object,
  schema: InvoiceSchema,

  parameters: [
    [...]
    coda.makeParameter({
      type: coda.ParameterType.Number,
      name: "clientId",
      description: "Client ID.",
    }),
    [...]
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: "issueDate",
      description: "Invoice issue date. Default: today.",
      optional: true,
    }),
    [...]
  ],

  varargParameters: [
    coda.makeParameter({
      type: coda.ParameterType.Number,
      name: "itemProductId",
      description: "Product ID of the invoice item.",
    }),
    coda.makeParameter({
      type: coda.ParameterType.Number,
      name: "itemQuantity",
      description: "Quantity of the invoice item.",
    }),
    coda.makeParameter({
      type: coda.ParameterType.Number,
      name: "itemTotalPriceGross",
      description: "Total gross price of the invoice item.",
    }),
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: "itemTax",
      description: "Tax of the invoice item.",
    }),
  ],

What solution would you recommend to make it possible to provide vararg params without the need to specify all the optional params too?

Hi @Mikolaj_Pastuszko - Welcome to the Coda community! Your assessment is correct, that once you add any vararg parameters you must specify all optional parameters. This is because vararg parameters can only be addressed positionally, not by name. There are probably ways to change the Coda Formula Language to address this in the long term, but at the moment this is an unavoidable consequence.

The approach I’ve recommended to other Pack makers is to move your vararg parameters to a separate helper formula, which bundles them up into JSON, and passes that JSON to a new optional parameter of your main formula. For example:

AddInvoice(My Account, "vat", 111, 111, 
  items: Items("abc", 10, 123.45, 20))

You could make the Items() formula use varargs, so you could specify multiple items at once:

AddInvoice(My Account, "vat", 111, 111, 
  items: Items("abc", 10, 123.45, 20, "def", 20, 45, 10))

But for this use it isn’t the easiest to read. It might make more sense to pass a List() of single Item() results.

AddInvoice(My Account, "vat", 111, 111, 
  items: List(Item("abc", 10, 123.45, 20), Item("def", 20, 45, 10)))

But if you expect these items to come from a table it probably makes more sense to make each parameter an Array or SparseArray type, so that the user could pass an entire column at once:

AddInvoice(My Account, "vat", 111, 111, 
  items: Items(ItemsTable.Name, ItemsTable.Quantity, ItemsTable.TotalPriceGross, ItemsTable.Tax))

@Leandro_Zubrezki’s Quickbooks Pack ran into this same issue, and he went with something sort of like the 2nd option I showed.

2 Likes

@Eric_Koleda Thanks a lot, this looks like a great solution! :pray: Very clear and easy to read!

1 Like

@Eric_Koleda, could you elaborate on how to define a formula parameter for your third case:

AddInvoice(My Account, "vat", 111, 111, 
  items: List(Item("abc", 10, 123.45, 20), Item("def", 20, 45, 10)))

I was trying to figure out how to pass variable number of parameters at run-time (i.e. not statically) to a formula, but thought that Coda didn’t have arrays of objects as parameters. Maybe I misunderstood your approach?

@Dan_Guberman - That’s correct, Packs can’t accept objects as parameters. In that example the Item() formula would output a string of JSON, and the items parameter would be of type StringArray so it could accept multiple JSON strings.

You can see this pattern used in my Mockaroo Pack. Take a look at the MockColumn formula and the columns parameter of the MockData sync table.

1 Like