Skip to content

Conversation

@jcortes
Copy link
Collaborator

@jcortes jcortes commented Aug 19, 2025

WHY

Resolves #3689

Summary by CodeRabbit

  • New Features

    • Added actions: Add Customer File, Add Reservation Companion, Add Reservation Product, Create Availability Block, Create Order, Create Task, Get Bill PDF, Get Rate Prices, Update Customer.
    • Added event sources: New Closed Bill, New Company, New Customer.
  • Enhancements

    • Fetch actions (Customers, Order Items, Products, Reservations) now offer granular filters, clearer summaries and improved pagination.
    • Improved pickers/options for accounts, bills, products, currencies, taxes, departments, credit cards, availability blocks, etc.
    • Update Reservation expanded with richer fields (reprice, fees, timing, resources, pricing, options).
  • Chores

    • Several patch version bumps and package version updated to 0.7.1.

@jcortes jcortes self-assigned this Aug 19, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 19, 2025

Walkthrough

Adds multiple new Mews actions and polling sources, extends the mews.app API surface and propDefinitions, replaces generic additionalFields with structured filter props across several fetch actions, introduces utils.parseArray, and bumps package and several module versions.

Changes

Cohort / File(s) Summary
New Actions
components/mews/actions/add-customer-file/add-customer-file.mjs, components/mews/actions/add-reservation-companion/add-reservation-companion.mjs, components/mews/actions/add-reservation-product/add-reservation-product.mjs, components/mews/actions/create-availability-block/create-availability-block.mjs, components/mews/actions/create-task/create-task.mjs, components/mews/actions/create-order/create-order.mjs, components/mews/actions/get-bill-pdf/get-bill-pdf.mjs, components/mews/actions/get-rate-prices/get-rate-prices.mjs, components/mews/actions/update-customer/update-customer.mjs
Adds new action modules exposing props and run() implementations that call corresponding mews.app methods and export success summaries.
Overhauled Fetch / Update Actions
components/mews/actions/fetch-customers/fetch-customers.mjs, components/mews/actions/fetch-order-items/fetch-order-items.mjs, components/mews/actions/fetch-products/fetch-products.mjs, components/mews/actions/fetch-reservations/fetch-reservations.mjs, components/mews/actions/update-reservation/update-reservation.mjs
Replaces additionalFields JSON parsing with explicit, granular filter props and structured payload construction; updates propDefinitions, conditional UTC blocks, and summary pluralization; update-reservation reshapes payload to ReservationUpdates with many new fields.
New Sources (Polling)
components/mews/sources/bill-closed/bill-closed.mjs, components/mews/sources/company-created/company-created.mjs, components/mews/sources/customer-created/customer-created.mjs
Adds polling sources that extend a common polling base and configure requester/result key/date/filter for Bills, Companies, and Customers.
App API & Option Providers
components/mews/mews.app.mjs
Adds many new endpoint wrapper methods (e.g., ordersCreate, billsGetPdf, ratesGetPricing, customersUpdate/search, currenciesGetAll, taxationsGetAll, availabilityBlocksCreate, tasksCreate, creditCardsGetAll) and new propDefinitions/options (productId, currency, taxRate, departmentId, accountType/accountId, billId, countryCode, creditCardId, availabilityBlockId); updates existing option mappings.
Utilities
components/mews/common/utils.mjs
Adds parseArray(input, maxDepth) and includes it in the default export alongside parseJson.
Version Bumps
components/mews/package.json, and multiple action/source files (e.g., .../cancel-reservation/..., .../create-reservation/..., various sources)
Package version and several module versions incremented (patch).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Action as Create Order Action
  participant App as Mews App
  participant API as Mews API

  User->>Action: Provide props (accountId, serviceId, productOrders, items, ...)
  Action->>App: ordersCreate({ $, data })
  App->>API: POST /orders/add
  API-->>App: Response (Order data)
  App-->>Action: Response
  Action-->>User: Summary "Successfully created order"
Loading
sequenceDiagram
  autonumber
  actor User
  participant Action as Update Reservation Action
  participant App as Mews App
  participant API as Mews API

  User->>Action: Provide props (reservationId, fields to update)
  Action->>App: reservationsUpdate({ $, data: { ReservationUpdates: [...] }, Reason/Reprice/... })
  App->>API: POST /reservations/update
  API-->>App: Response
  App-->>Action: Response
  Action-->>User: Summary "Successfully updated reservation {id}"
Loading
sequenceDiagram
  autonumber
  participant Poller as Platform Poller
  participant Source as Bill Closed Source
  participant App as Mews App
  participant API as Mews API

  loop every poll interval
    Poller->>Source: invoke poll
    Source->>App: billsGetAll({ $, data: { State: "Closed", ClosedUtc: { StartUtc, EndUtc } } })
    App->>API: POST /bills/getAll
    API-->>App: { Bills: [...] }
    App-->>Source: Bills
    Source-->>Poller: emit new Bill events (by ClosedUtc)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Assessment against linked issues

Objective Addressed Explanation
Triggers: New Closed Bill, New Company Event, New Customer Event (#3689)
Triggers: Pagination for polling (#3689) The diff adds polling sources but does not clearly show pagination implementation or page token handling in the polling base.
Actions: Add Customer File; Add Reservation Companion; Add Reservation Product; Create Availability Block; Create New Task; Create Order; Update Customer; Update Reservation; Get Bill by Id; Get Rate Prices (#3689)
Actions: Create Customer; Search Customer by Email/Id/Name (#3689) No Create Customer action added; customersSearch exists in app but no exposed action for search.

Possibly related PRs

  • [Components] Mews #17998 — modifies the same Mews connector components (actions, sources, mews.app, utils); likely directly related.

Suggested reviewers

  • lcaresia
  • luancazarine

Poem

In burrows of code I nibbled keys,
I parsed arrays and tended trees.
Bills close, tasks sprout, orders hum—
I hop with joy for features come.
Carrots, props, and tiny sums,
Ship it now — the rabbit drums. 🥕🐇

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mews-new-component-second-part

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@vercel
Copy link

vercel bot commented Aug 19, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
pipedream-docs Ignored Ignored Aug 27, 2025 2:37pm
pipedream-docs-redirect-do-not-edit Ignored Ignored Aug 27, 2025 2:37pm

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (25)
components/mews/actions/cancel-reservation/cancel-reservation.mjs (1)

18-33: Nit: Improve the action summary for traceability and guard against empty input

Including the reservationId in the summary helps with observability in runs. Adding a lightweight guard provides clearer error messages if the prop is missing at runtime (even if propDefinition generally enforces presence).

   async run({ $ }) {
     const {
       app,
       reservationId,
     } = this;
+    if (!reservationId) {
+      throw new Error("reservationId is required");
+    }
     const response = await app.reservationsCancel({
       $,
       data: {
         ReservationIds: [
           reservationId,
         ],
       },
     });
-    $.export("summary", "Successfully cancelled reservation");
+    $.export("summary", `Successfully cancelled reservation ${reservationId}`);
     return response;
   },
components/mews/sources/company-created/company-created.mjs (1)

6-6: Align description phrasing with existing sources

Match the phrasing used elsewhere (created + polling).

-  description: "Emit new event when a company is created",
+  description: "Emit new companies as they are created (polling)",
components/mews/common/utils.mjs (2)

42-63: Guard recursion depth and decrement consistently in parseArray

parseArray doesn’t honor maxDepth unless the input is a stringified array, and recursion for array inputs doesn’t decrement depth. This can lead to excessive recursion on deeply nested arrays and makes maxDepth ineffective in non-string cases.

Apply this diff to add a depth guard and consistently decrement depth on nested calls:

-function parseArray (input, maxDepth = 100) {
-  if (typeof input === "string") {
+function parseArray (input, maxDepth = 100) {
+  if (maxDepth <= 0) {
+    return input;
+  }
+  if (typeof input === "string") {
     const trimmed = input.trim();
     if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
       try {
         const parsed = JSON.parse(trimmed);
         if (Array.isArray(parsed)) {
-          return parsed.map((item) => parseArray(item, maxDepth - 1));
+          return parsed.map((item) => parseArray(item, maxDepth - 1));
         }
       } catch (e) {
         throw new Error(`Invalid JSON array format: ${e.message}`);
       }
     }
-    return parseJson(input, maxDepth);
+    return parseJson(input, maxDepth - 1);
   }
 
   if (Array.isArray(input)) {
-    return input.map((item) => parseArray(item, maxDepth));
+    return input.map((item) => parseArray(item, maxDepth - 1));
   }
 
   return input;
 }

42-63: Avoid cross-module ambiguity: parseArray semantics differ from openai/common/helpers.mjs

There’s another parseArray in components/openai/common/helpers.mjs that only maps parseJson over arrays and doesn’t parse stringified arrays. Sharing the same name but different behavior can confuse maintainers.

  • Add a short JSDoc describing the stricter semantics here, or
  • Consider a more explicit name (e.g., parseArrayDeep or parseArrayStrict) to prevent future misuse.
components/mews/actions/add-customer-file/add-customer-file.mjs (1)

60-61: Harden summary against varying response shapes (FileId vs Id)

If the API response doesn’t always expose FileId (or nests it), the summary may print “undefined”. Prefer a resilient fallback.

Apply this diff to guard against shape differences:

-    $.export("summary", `Successfully added file with ID \`${response.FileId}\``);
-    return response;
+    const fileId = response?.FileId ?? response?.Id ?? response?.File?.Id;
+    $.export("summary", fileId
+      ? `Successfully added file with ID \`${fileId}\``
+      : "Successfully added file");
+    return response;

If you have the exact schema of customersAddFile’s response handy, I can tailor this to the definitive field.

components/mews/actions/create-task/create-task.mjs (1)

63-64: Improve summary with task identifier when available

Returning just a generic success message makes it harder to link runs to created tasks.

Apply this diff to include the ID when present:

-    $.export("summary", "Successfully created task");
-    return response;
+    const taskId = response?.TaskId ?? response?.Id;
+    $.export("summary", taskId
+      ? `Successfully created task (ID: ${taskId})`
+      : "Successfully created task");
+    return response;
components/mews/actions/fetch-customers/fetch-customers.mjs (3)

139-176: Auto-include Deleted state when DeletedUtc filters are used

When DeletedUtc filters are provided without including "Deleted" in ActivityStates, the API may return nothing. We can include it automatically to reduce footguns.

Apply this diff to compute a safe ActivityStates:

   async run({ $ }) {
     const {
       app,
       createdStartUtc,
       createdEndUtc,
       updatedStartUtc,
       updatedEndUtc,
       deletedStartUtc,
       deletedEndUtc,
       activityStates,
       customerIds,
       companyIds,
       emails,
       firstNames,
       lastNames,
       extentCustomers,
       extentAddresses,
     } = this;

+    const needsDeleted = Boolean(deletedStartUtc || deletedEndUtc);
+    const finalActivityStates = needsDeleted
+      ? Array.from(new Set([...(activityStates || []), "Deleted"]))
+      : activityStates;
 
     const items = await app.paginate({
       requester: app.customersGetAll,
       requesterArgs: {
         $,
         data: {
@@
-          ActivityStates: activityStates,
+          ActivityStates: finalActivityStates,
           CustomerIds: customerIds,
           CompanyIds: companyIds,
-          Emails: emails,
+          Emails: emails?.filter(Boolean),
           FirstNames: firstNames,
           LastNames: lastNames,
         },
       },
       resultKey: "Customers",
     });

The Emails filter also now ignores empty values that may come from missing emails in options.


119-137: Optional: Enforce at least one filter to avoid massive unbounded fetches

The Mews Customers GetAll endpoint often expects some filter (or limits) to avoid huge responses. Your description hints “CustomerIds required if no other filter is provided,” but the code doesn’t enforce it.

Add a guard before paginate:

   async run({ $ }) {
     const {
@@
       extentAddresses,
     } = this;
 
+    if (
+      !createdStartUtc && !createdEndUtc &&
+      !updatedStartUtc && !updatedEndUtc &&
+      !deletedStartUtc && !deletedEndUtc &&
+      !activityStates?.length &&
+      !customerIds?.length &&
+      !companyIds?.length &&
+      !emails?.length &&
+      !firstNames?.length &&
+      !lastNames?.length
+    ) {
+      throw new Error("Provide at least one filter (e.g., CustomerIds, Dates, Email, Name) to fetch customers.");
+    }

If the API does allow fully unfiltered pagination safely in your environment, feel free to skip this and keep the current behavior.


82-91: Filter out customers without Email in the custom customerId propDefinition

The override only replaces the mapper but still uses the base options method, which returns every customer—including those with no Email—so you can end up with undefined values in your dropdown. To prevent that, override the options method to filter out customers missing an email:

 propDefinition: [
   app,
   "customerId",
-  () => ({
-    mapper: (customer) => ({
-      label: `${customer.FirstName} ${customer.LastName}`,
-      value: customer.Email,
-    }),
-  }),
+  () => ({
+    async options() {
+      const { Customers: customers } = await this.customersGetAll();
+      return customers
+        .filter(customer => customer.Email)               // remove entries without email
+        .map(customer => ({
+          label: `${customer.FirstName} ${customer.LastName}`,
+          value: customer.Email,
+        }));
+    },
+  }),
 ],

This ensures only customers with a non-falsy Email appear in the list.

components/mews/actions/fetch-order-items/fetch-order-items.mjs (3)

119-124: Offer currency options via app propDefinition for consistency

Leverage the app’s currency options to drive valid ISO-4217 selections and align with other actions.

-    currency: {
-      type: "string",
-      label: "Currency",
-      description: "ISO-4217 code of the Currency the item costs should be converted to.",
-      optional: true,
-    },
+    currency: {
+      type: "string",
+      label: "Currency",
+      description: "ISO 4217 currency code to convert item costs to.",
+      optional: true,
+      propDefinition: [
+        app,
+        "currency",
+      ],
+    },

89-98: Broaden Service Order IDs options to include Product Service Orders, not just Reservations

Service orders may be Product Service Orders or Reservations. The current propDefinition only exposes Reservations, which can mislead users and hide valid IDs.

     serviceOrderIds: {
       type: "string[]",
       label: "Service Order IDs",
       description: "Unique identifiers of the service orders (product service orders or reservations). Required if no other filter is provided. Max 1000 items.",
       optional: true,
-      propDefinition: [
-        app,
-        "reservationId",
-      ],
+      async options() {
+        const [{ Reservations }, { ProductServiceOrders }] = await Promise.all([
+          this.app.reservationsGetAll(),
+          this.app.productServiceOrdersGetAll(),
+        ]);
+        const reservationOptions = (Reservations || []).map(({ Id: value, Number: label }) => ({
+          label: `Reservation ${label}`,
+          value,
+        }));
+        const productServiceOrderOptions = (ProductServiceOrders || []).map(({ Id: value, Number: label }) => ({
+          label: `Product Service Order ${label}`,
+          value,
+        }));
+        return [
+          ...reservationOptions,
+          ...productServiceOrderOptions,
+        ];
+      },
     },

If you prefer to keep the propDefinition approach, we can instead introduce a separate productServiceOrderIds prop next to serviceOrderIds. Want me to push that version?


207-216: Optionally omit undefined top-level filters for cleaner requests

Axios/JSON.stringify drops undefined, so this works as-is. Still, explicitly omitting empty filters reduces payload noise and avoids accidental null-ish values if serialization changes.

Example pattern (not exhaustive):

-          EnterpriseIds: enterpriseIds,
+          ...(enterpriseIds?.length && { EnterpriseIds: enterpriseIds }),

Apply similarly to other top-level arrays/fields if you like this style.

components/mews/actions/update-customer/update-customer.mjs (3)

156-184: Clarify Classifications: remove “JSON” wording; values are sent as an array of enums

The prop is a string[] with static options; asking for JSON strings is misleading.

     classifications: {
       type: "string[]",
       label: "Classifications",
-      description: "New classifications of the customer in JSON format. Each classification should be a JSON string with the classification ID.",
+      description: "Classification flags of the customer. Select one or more values.",
       optional: true,
       options: [
         "None",
         "PaymasterAccount",
         "Blacklist",
         "Media",
         "LoyaltyProgram",
         "PreviousComplaint",
         "Returning",
         "Staff",
         "FriendOrFamily",
         "TopManagement",
         "Important",
         "VeryImportant",
         "Problematic",
         "Cashlist",
         "DisabledPerson",
         "Military",
         "Airline",
         "HealthCompliant",
         "InRoom",
         "WaitingForRoom",
         "Student",
       ],
     },

203-208: Align Italian Destination Code description with implementation (accepts a string, wrapped as { Value })

Implementation wraps a string into { Value }, so the description shouldn’t ask for JSON.

     italianDestinationCode: {
       type: "string",
       label: "Italian Destination Code",
-      description: "New Italian destination code of customer in JSON format. Should include 'Value' property.",
+      description: "Italian destination code. This value will be wrapped as { Value: <code> } in the request.",
       optional: true,
     },

216-284: Optional: Validate that at least one field is being updated

Prevent no-op updates by checking that at least one updatable field is provided besides customerId.

Example insertion right before the API call:

   async run({ $ }) {
     const {
       app,
       customerId,
       title,
       firstName,
       lastName,
       secondLastName,
       nationalityCode,
       sex,
       birthDate,
       birthPlace,
       occupation,
       email,
       phone,
       loyaltyCode,
       notes,
       carRegistrationNumber,
       dietaryRequirements,
       taxIdentificationNumber,
       companyId,
       address,
       classifications,
       options,
       italianDestinationCode,
       italianFiscalCode,
     } = this;

+    const hasUpdates = [
+      title, firstName, lastName, secondLastName, nationalityCode, sex, birthDate,
+      birthPlace, occupation, email, phone, loyaltyCode, notes, carRegistrationNumber,
+      dietaryRequirements, taxIdentificationNumber, companyId, address,
+      classifications?.length, options?.length, italianDestinationCode, italianFiscalCode,
+    ].some((v) => v !== undefined && v !== null && (Array.isArray(v) ? v.length > 0 : true));
+
+    if (!hasUpdates) {
+      throw new Error("Provide at least one field to update.");
+    }
components/mews/mews.app.mjs (1)

137-142: Travel Agency ID has no options now; confirm intended

If travel agencies are represented under Companies, consider wiring options like companyId for consistency, or remove if unused.

components/mews/actions/get-rate-prices/get-rate-prices.mjs (2)

49-53: Avoid sending undefined optional fields (conditionally include ProductId).

Guard the optional ProductId to prevent sending an undefined field to the API.

         RateId: rateId,
-        ProductId: productId,
+        ...(productId && { ProductId: productId }),
         FirstTimeUnitStartUtc: firstTimeUnitStartUtc,
         LastTimeUnitStartUtc: lastTimeUnitStartUtc,

37-46: Validate the time interval before calling the API.

Basic checks reduce avoidable API errors due to invalid/unsorted timestamps.

   async run({ $ }) {
     const {
       app,
       rateId,
       productId,
       firstTimeUnitStartUtc,
       lastTimeUnitStartUtc,
     } = this;

+    // Basic interval validation
+    const start = new Date(firstTimeUnitStartUtc);
+    const end = new Date(lastTimeUnitStartUtc);
+    if (Number.isNaN(start.valueOf()) || Number.isNaN(end.valueOf()) || end < start) {
+      throw new Error("Invalid time interval: ensure valid ISO 8601 UTC timestamps and that the end is not before the start.");
+    }
components/mews/actions/create-availability-block/create-availability-block.mjs (1)

46-57: Optional: validate mutual exclusivity/prioritization of ReleasedUtc vs RollingReleaseOffset.

Docs say ReleasedUtc takes precedence over RollingReleaseOffset. Consider warning or rejecting when both are provided to avoid user confusion.

Example guard (place after destructuring):

  • If both provided, either throw or log a warning before request.
components/mews/actions/add-reservation-product/add-reservation-product.mjs (1)

63-72: Avoid mixing propDefinition with an explicit type for the same prop.

Rely on the propDefinition’s type to avoid conflicts or UI confusion.

-    unitAmountTaxCodes: {
-      type: "string[]",
+    unitAmountTaxCodes: {
       label: "Unit Amount - Tax Codes",
       description: "Codes of Tax rates to be applied to the item. (Note, you can only define one tax when sending **Gross Value**. For multiple taxes, use **Net Value**)",
       optional: true,
       propDefinition: [
         app,
         "taxRate",
       ],
     },
components/mews/actions/get-bill-pdf/get-bill-pdf.mjs (2)

52-57: Consider validating printReason length (max 255) in the action

The API enforces max length 255 for France LE. Adding a local check prevents avoidable API errors.

Apply a lightweight guard:

     printReason,
   } = this;

+  if (printReason && printReason.length > 255) {
+    throw new Error("Print Reason must be 255 characters or fewer.");
+  }

59-80: Use $.exportFile with arraybuffer responseType to handle binary PDF

The billsGetPdf call delegates to the Pipedream axios wrapper without an explicit responseType, so by default it will try to parse JSON and fail on a PDF payload. It also returns the full Axios response instead of the raw bytes needed to write a file.

• File: components/mews/actions/get-bill-pdf/get-bill-pdf.mjs
• Lines: 59–80

Suggested change:

   async run({ $ }) {
     const {
       app,
       billId,
       billPrintEventId,
       pdfTemplate,
       printReason,
     } = this;

-    const response = await app.billsGetPdf({
+    const { data } = await app.billsGetPdf({
+      $,
+      responseType: "arraybuffer",      // ensure binary PDF
       data: {
         BillId: billId,
         BillPrintEventId: billPrintEventId,
         PdfTemplate: pdfTemplate,
         PrintReason: printReason,
       },
     });

-    $.export("summary", `Successfully retrieved PDF for bill ${billId}`);
-    return response;
+    const filename = `bill-${billId}${pdfTemplate ? `-${pdfTemplate}` : ""}.pdf`;
+    await $.exportFile(filename, data);
+    $.export("summary", `Successfully retrieved PDF for bill ${billId}`);
+    return { billId, filename };
   },
components/mews/actions/fetch-products/fetch-products.mjs (1)

48-53: Honor API default for IncludeDefault, or set it explicitly based on ProductIds

The description states: if Product IDs are provided, Include Default defaults to true; otherwise false. Today, you always pass IncludeDefault: includeDefault which is undefined when not set. If the app request builder strips undefined, the API will apply its default. If not, better to compute the default explicitly.

Apply:

   } = this;

-  const items = await app.paginate({
+  const effectiveIncludeDefault =
+    includeDefault ?? ((Array.isArray(productIds) && productIds.length > 0) ? true : false);
+
+  const items = await app.paginate({
       requester: app.productsGetAll,
       requesterArgs: {
         $,
-        data: {
-          ServiceIds: serviceIds,
+        data: {
+          ServiceIds: serviceIds,
           ...(updatedStartUtc || updatedEndUtc) && {
             UpdatedUtc: {
               StartUtc: updatedStartUtc,
               EndUtc: updatedEndUtc,
             },
           },
-          EnterpriseIds: enterpriseIds,
-          ProductIds: productIds,
-          IncludeDefault: includeDefault,
+          ...(enterpriseIds?.length) && { EnterpriseIds: enterpriseIds },
+          ...(productIds?.length) && { ProductIds: productIds },
+          ...(effectiveIncludeDefault !== undefined) && { IncludeDefault: effectiveIncludeDefault },
         },
       },
       resultKey: "Products",
     });

This also avoids sending empty arrays/undefined keys, matching patterns used in other actions in this PR.

components/mews/actions/update-reservation/update-reservation.mjs (2)

12-29: Top-level flags should be included conditionally to avoid sending undefined

Reason, Reprice, and ApplyCancellationFee are always included, even when undefined. If the client doesn’t strip undefined, this may cause payload issues or unintended defaults. Other actions in the repo conditionally include fields.

   data: {
-    Reason: reason,
-    Reprice: reprice,
-    ApplyCancellationFee: applyCancellationFee,
+    ...(reason && { Reason: reason }),
+    ...(reprice !== undefined && { Reprice: reprice }),
+    ...(applyCancellationFee !== undefined && { ApplyCancellationFee: applyCancellationFee }),
     ReservationUpdates: [

37-60: Clarify how to clear fields that require explicit null (Value: null)

For fields like channelNumber, startUtc, endUtc, releasedUtc, etc., the docs mention “Pass an object with Value or null if the field should not be updated.” The current implementation only supports setting a new value; there’s no way for users to send a null to clear. Consider a pattern (used in other integrations) that adds a boolean “clearX” prop or accepts JSON input for the Value.

If you want, I can draft a minimal “clear” pattern for the string fields to support Value: null updates.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 09e9f3f and 74feaff.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (28)
  • components/mews/actions/add-customer-file/add-customer-file.mjs (1 hunks)
  • components/mews/actions/add-reservation-companion/add-reservation-companion.mjs (1 hunks)
  • components/mews/actions/add-reservation-product/add-reservation-product.mjs (1 hunks)
  • components/mews/actions/cancel-reservation/cancel-reservation.mjs (1 hunks)
  • components/mews/actions/create-availability-block/create-availability-block.mjs (1 hunks)
  • components/mews/actions/create-order/create-order.mjs (1 hunks)
  • components/mews/actions/create-reservation/create-reservation.mjs (1 hunks)
  • components/mews/actions/create-task/create-task.mjs (1 hunks)
  • components/mews/actions/fetch-customers/fetch-customers.mjs (1 hunks)
  • components/mews/actions/fetch-order-items/fetch-order-items.mjs (1 hunks)
  • components/mews/actions/fetch-products/fetch-products.mjs (1 hunks)
  • components/mews/actions/fetch-reservations/fetch-reservations.mjs (1 hunks)
  • components/mews/actions/get-bill-pdf/get-bill-pdf.mjs (1 hunks)
  • components/mews/actions/get-rate-prices/get-rate-prices.mjs (1 hunks)
  • components/mews/actions/update-customer/update-customer.mjs (1 hunks)
  • components/mews/actions/update-reservation/update-reservation.mjs (1 hunks)
  • components/mews/common/utils.mjs (1 hunks)
  • components/mews/mews.app.mjs (9 hunks)
  • components/mews/package.json (1 hunks)
  • components/mews/sources/bill-closed/bill-closed.mjs (1 hunks)
  • components/mews/sources/company-created/company-created.mjs (1 hunks)
  • components/mews/sources/customer-created/customer-created.mjs (1 hunks)
  • components/mews/sources/order-item-created/order-item-created.mjs (1 hunks)
  • components/mews/sources/order-item-updated/order-item-updated.mjs (1 hunks)
  • components/mews/sources/product-service-order-created/product-service-order-created.mjs (1 hunks)
  • components/mews/sources/reservation-cancelled/reservation-cancelled.mjs (1 hunks)
  • components/mews/sources/reservation-created/reservation-created.mjs (1 hunks)
  • components/mews/sources/reservation-updated/reservation-updated.mjs (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (12)
components/mews/common/utils.mjs (1)
components/openai/common/helpers.mjs (1)
  • parseArray (82-88)
components/mews/actions/create-order/create-order.mjs (3)
components/mews/actions/add-reservation-product/add-reservation-product.mjs (1)
  • response (88-103)
components/mews/actions/create-reservation/create-reservation.mjs (1)
  • response (118-157)
components/mews/actions/update-customer/update-customer.mjs (1)
  • response (244-279)
components/mews/actions/fetch-products/fetch-products.mjs (5)
components/mews/actions/fetch-customers/fetch-customers.mjs (1)
  • items (139-175)
components/mews/actions/fetch-order-items/fetch-order-items.mjs (1)
  • items (172-219)
components/mews/actions/fetch-reservations/fetch-reservations.mjs (1)
  • items (228-290)
components/mews/sources/common/polling.mjs (1)
  • items (97-109)
components/wix_api_key/actions/add-products-to-collection/add-products-to-collection.mjs (1)
  • productIds (37-39)
components/mews/actions/fetch-customers/fetch-customers.mjs (4)
components/mews/actions/fetch-products/fetch-products.mjs (1)
  • items (66-84)
components/mews/actions/fetch-order-items/fetch-order-items.mjs (1)
  • items (172-219)
components/mews/actions/fetch-reservations/fetch-reservations.mjs (1)
  • items (228-290)
components/mews/sources/common/polling.mjs (1)
  • items (97-109)
components/mews/actions/add-reservation-product/add-reservation-product.mjs (3)
components/mews/actions/add-reservation-companion/add-reservation-companion.mjs (1)
  • response (33-39)
components/mews/actions/create-order/create-order.mjs (1)
  • response (138-150)
components/mews/actions/create-reservation/create-reservation.mjs (1)
  • response (118-157)
components/mews/actions/create-availability-block/create-availability-block.mjs (2)
components/mews/actions/create-reservation/create-reservation.mjs (1)
  • response (118-157)
components/mews/actions/get-rate-prices/get-rate-prices.mjs (1)
  • response (46-54)
components/mews/actions/update-customer/update-customer.mjs (3)
components/mews/actions/add-customer-file/add-customer-file.mjs (1)
  • response (50-58)
components/mews/actions/create-order/create-order.mjs (1)
  • response (138-150)
components/mews/actions/update-reservation/update-reservation.mjs (1)
  • response (227-331)
components/mews/actions/fetch-order-items/fetch-order-items.mjs (4)
components/mews/actions/fetch-products/fetch-products.mjs (1)
  • items (66-84)
components/mews/actions/fetch-customers/fetch-customers.mjs (1)
  • items (139-175)
components/mews/actions/fetch-reservations/fetch-reservations.mjs (1)
  • items (228-290)
components/mews/sources/common/polling.mjs (1)
  • items (97-109)
components/mews/actions/get-rate-prices/get-rate-prices.mjs (2)
components/mews/actions/create-availability-block/create-availability-block.mjs (1)
  • response (167-198)
components/mews/mews.app.mjs (1)
  • response (551-560)
components/mews/actions/fetch-reservations/fetch-reservations.mjs (4)
components/mews/actions/fetch-products/fetch-products.mjs (1)
  • items (66-84)
components/mews/actions/fetch-customers/fetch-customers.mjs (1)
  • items (139-175)
components/mews/actions/fetch-order-items/fetch-order-items.mjs (1)
  • items (172-219)
components/mews/sources/common/polling.mjs (1)
  • items (97-109)
components/mews/actions/add-reservation-companion/add-reservation-companion.mjs (2)
components/mews/actions/cancel-reservation/cancel-reservation.mjs (1)
  • response (23-30)
components/mews/actions/add-reservation-product/add-reservation-product.mjs (1)
  • response (88-103)
components/mews/actions/update-reservation/update-reservation.mjs (3)
components/mews/actions/create-availability-block/create-availability-block.mjs (1)
  • response (167-198)
components/mews/actions/create-reservation/create-reservation.mjs (1)
  • response (118-157)
components/mews/actions/update-customer/update-customer.mjs (1)
  • response (244-279)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Lint Code Base
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
  • GitHub Check: pnpm publish
🔇 Additional comments (31)
components/mews/package.json (1)

3-3: Version bump looks good

Patch version bump aligns with the set of additive changes across actions/sources. No issues spotted.

components/mews/sources/reservation-updated/reservation-updated.mjs (1)

8-8: Patch version bump only — LGTM

No functional changes introduced. The polling configuration (UpdatedUtc for both sort and filter fields) remains consistent with the base source expectations.

components/mews/sources/product-service-order-created/product-service-order-created.mjs (1)

8-8: Patch version bump only — LGTM

No behavioral changes. Polling continues to target CreatedUtc, which matches the event semantics for “created” sources.

components/mews/actions/cancel-reservation/cancel-reservation.mjs (1)

7-7: Patch version bump only — LGTM

No logic changes. Action remains backward-compatible.

components/mews/sources/order-item-updated/order-item-updated.mjs (1)

8-8: Patch version bump only — LGTM

No runtime or API changes. Polling fields remain consistent with “updated” semantics.

components/mews/sources/reservation-created/reservation-created.mjs (1)

8-8: LGTM: Patch version bump only

Version bump to 0.0.2 with no behavioral changes looks good and aligns with the broader release.

components/mews/sources/order-item-created/order-item-created.mjs (1)

8-8: LGTM: Patch version bump only

Version bump to 0.0.2 with no behavioral changes looks good and is consistent with other sources in this PR.

components/mews/actions/create-reservation/create-reservation.mjs (1)

8-8: LGTM: Patch version bump only

No logic changes alongside the version bump; looks good.

components/mews/sources/reservation-cancelled/reservation-cancelled.mjs (2)

8-8: LGTM: Patch version bump only

No functional changes; version bump is consistent with the rest of the sources.


24-29: All usages of “CanceledUtc” and “Canceled” are correct
Verified across the codebase and Mews Connector API docs that the cancellation timestamp property is “CanceledUtc” (single L) and the reservation state string is “Canceled” (single L). No changes required.

components/mews/sources/company-created/company-created.mjs (2)

1-31: Solid new polling source; accessor methods match Mews conventions

The requester, result key, ID resolver, and date fields are consistent with other Mews sources. Good use of the common polling base.


12-14: mews.app.mjs exposes required methods
The app module defines companiesGetAll, orderItemsGetAll, and reservationsGetAll (see lines 355–359, 391–394, 457–460), so the source’s call to this.app.companiesGetAll is valid. No changes needed.

components/mews/common/utils.mjs (1)

51-55: Confirm strict failure on invalid JSON array is desired UX

Unlike parseJson (which silently returns the original string on parse failure), parseArray throws on invalid JSON arrays. This will surface as a hard error in actions that use it. If that’s intentional (to fail fast when an array is required), great—just confirm this difference is expected across all new usages in this PR.

Would you like this to be strict (throw) or permissive (fallback to original string)? If permissive, I can provide a patch to mirror parseJson’s behavior.

components/mews/sources/customer-created/customer-created.mjs (1)

3-31: LGTM: Polling source correctly targets CreatedUtc and maps IDs

The source aligns with the common polling base, uses CreatedUtc for both emitted timestamp and filter, and maps Id safely. Looks good.

components/mews/actions/fetch-reservations/fetch-reservations.mjs (1)

228-290: LGTM on explicit payload construction and pagination

Clear conditional blocks for every UTC filter, top-level array filters, and pluralized summary. This mirrors the updated pattern across other fetch actions.

components/mews/mews.app.mjs (3)

7-27: General: option mappings LGTM

Reservations/Services/Resources/Rates/Companies/Departments/Countries/Credit Cards/Availability Blocks selectors correctly map label/value to API shapes. Nice improvement over raw payload fields.

Also applies to: 83-91, 111-119, 127-135, 149-157, 159-173, 174-207, 209-225, 262-276, 278-291, 311-326


535-573: Paginate helper is solid

Cursor-based pagination with maxRequests guard and consistent Limitation payload construction looks good.


451-456: Confirm ratesGetPricing endpoint path usage

  • Repo-local search found only the single reference at components/mews/mews.app.mjs:453
  • No typos or duplicate paths detected in the codebase

Please double-check that “/rates/getPricing” exactly matches the latest Mews API documentation.

components/mews/sources/bill-closed/bill-closed.mjs (1)

3-36: LGTM: polling source correctly filters Closed bills and uses ClosedUtc for windowing

Requester/resultKey/date field wiring matches the shared polling base. Static State: "Closed" filter is appropriate.

components/mews/actions/get-rate-prices/get-rate-prices.mjs (1)

46-55: Confirm inclusion of ServiceId in ratesGetPricing request

The ratesGetPricing wrapper simply spreads your args into the request – it doesn’t enforce any particular payload shape:

• In components/mews/mews.app.mjs (lines 451–454), the implementation is:

ratesGetPricing(args = {}) {
  return this._makeRequest({
    path: "/rates/getPricing",
    ...args,
  });
},

• No other call sites supply a ServiceId, and there’s no default or validation in the wrapper.

Please verify against the official Mews API documentation whether the /rates/getPricing endpoint requires a ServiceId alongside your RateId (even if it’s optional). If it is required, update your action to include:

 data: {
   RateId: rateId,
   ProductId: productId,
+  ServiceId: serviceId,     // <— add this if required by the API
   FirstTimeUnitStartUtc: firstTimeUnitStartUtc,
   LastTimeUnitStartUtc: lastTimeUnitStartUtc,
 },
components/mews/actions/add-reservation-companion/add-reservation-companion.mjs (2)

33-41: Straightforward, consistent API call. LGTM.

The request shape matches the endpoint expectations and summary is useful.


33-41: reservationsAddCompanion signature verified

  • In components/mews/mews.app.mjs (lines 379–382), reservationsAddCompanion(args = {}) merges ...args into the request.
  • The call in add-reservation-companion.mjs supplies $ and data: { ReservationId, CustomerId }, matching the wrapper’s signature.

No changes required.

components/mews/actions/get-bill-pdf/get-bill-pdf.mjs (1)

24-51: Good use of enumerated PDF template options

Clear labels and constrained values reduce input errors and align with the API’s expected enum.

components/mews/actions/fetch-products/fetch-products.mjs (3)

7-7: Version bump looks appropriate for the breaking prop changes

Switching from additionalFields to structured props and changing the public surface warrants 0.0.2. Good.


11-19: Confirm multi-select compatibility for serviceIds propDefinition

Using propDefinition: [app, "serviceId"] with type: "string[]" is a common pattern for multi-selects. Ensure the underlying propDefinition supplies options that work with array types.

If needed, align with patterns used in other actions that expose multi-select IDs to ensure consistent UX.


86-89: Nice touch on pluralized summary

Readable step output helps. LGTM.

components/mews/actions/update-reservation/update-reservation.mjs (5)

6-9: Public contract change is justified and documented

Version bump to 0.0.2 with enhanced props and link to docs is appropriate.


61-84: personCounts typed as string[] is fine if utils.parseArray handles JSON strings per item

Given the examples, users will likely paste JSON per element. Confirm utils.parseArray maps a string[] like ['{"AgeCategoryId":"...","Count":2}', ...] to an object[] as required by the API.

If not, switch the prop to a single JSON string and parse via utils.parseJson for fewer UX footguns.


137-172: timeUnitPrices parsing mirrors personCounts — confirm utils.parseArray handles nested Amount objects

Same concern: ensure utils.parseArray correctly parses complex nested objects per array item and surfaces a good error message if parsing fails.

Optional: add a quick try/catch around parsing to throw a nicer validation error before the API call.


311-327: Correct handling of booleans with explicit undefined checks

Using !== undefined ensures false values aren’t dropped. Nice.


333-334: Good summary including the reservation ID

Helpful for run history.

@jcortes jcortes force-pushed the mews-new-component-second-part branch 2 times, most recently from cb5fa86 to 37d3820 Compare August 20, 2025 15:18
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (5)
components/mews/actions/create-order/create-order.mjs (2)

12-17: Remove unused accountType propDefinition

This prop isn’t used in run() and isn’t supported by the Mews Add order API. Leaving it confuses users.

     app,
-    accountType: {
-      propDefinition: [
-        app,
-        "accountType",
-      ],
-    },

138-149: Parse arrays once, validate at least one of ProductOrders/Items is provided, and omit undefineds

Avoid double parsing, proactively prevent empty payload errors, and don’t send undefined fields.

-    const response = await app.ordersCreate({
-      $,
-      data: {
-        AccountId: accountId,
-        ServiceId: serviceId,
-        BillId: billId,
-        LinkedReservationId: linkedReservationId,
-        ConsumptionUtc: consumptionUtc,
-        Notes: notes,
-        ProductOrders: utils.parseArray(productOrders),
-        Items: utils.parseArray(items),
-      },
-    });
+    const parsedProductOrders = utils.parseArray(productOrders);
+    const parsedItems = utils.parseArray(items);
+    if ((!parsedProductOrders || parsedProductOrders.length === 0)
+      && (!parsedItems || parsedItems.length === 0)) {
+      throw new Error("Provide at least one Product Order or Item.");
+    }
+
+    const response = await app.ordersCreate({
+      $,
+      data: {
+        AccountId: accountId,
+        ServiceId: serviceId,
+        ...(billId && { BillId: billId }),
+        ...(linkedReservationId && { LinkedReservationId: linkedReservationId }),
+        ...(consumptionUtc && { ConsumptionUtc: consumptionUtc }),
+        ...(notes && { Notes: notes }),
+        ...(parsedProductOrders?.length && { ProductOrders: parsedProductOrders }),
+        ...(parsedItems?.length && { Items: parsedItems }),
+      },
+    });
components/mews/mews.app.mjs (2)

235-261: LGTM: options handlers correctly use this.* app methods.

The previous this.app.* bug is resolved. Using this.companiesGetAll() / this.customersGetAll() is correct within propDefinitions.


294-310: Align creditCardId description with actual type and behavior.

Type is string and options return Id strings, but the description still asks for an object with Value. Actions wrap the Id at call-time.

     creditCardId: {
       type: "string",
       label: "Credit Card ID",
-      description: "Identifier of CreditCard belonging to Customer who owns the reservation. Pass an object with 'Value' property or null if the credit card should not be updated.",
+      description: "Identifier of a Credit Card belonging to the reservation owner. Provide the Id; actions will wrap it as { Value: <Id> } when sending to the API.",
       optional: true,
components/mews/actions/add-reservation-product/add-reservation-product.mjs (1)

75-86: Validate unit amount: require exactly one of GrossValue or NetValue.

Prevent avoidable API errors by enforcing this constraint up front.

   async run({ $ }) {
     const {
       app,
       reservationId,
       productId,
       startUtc,
       endUtc,
       count,
       unitAmountGrossValue,
       unitAmountNetValue,
       unitAmountCurrency,
       unitAmountTaxCodes,
     } = this;

+    if (count !== undefined && count <= 0) {
+      throw new Error("Count must be a positive integer.");
+    }
+
+    const hasGross = unitAmountGrossValue !== undefined && unitAmountGrossValue !== null && unitAmountGrossValue !== "";
+    const hasNet = unitAmountNetValue !== undefined && unitAmountNetValue !== null && unitAmountNetValue !== "";
+    if (hasGross && hasNet) {
+      throw new Error("Provide either Unit Amount - Gross Value or Unit Amount - Net Value, not both.");
+    }
+    if (!hasGross && !hasNet && (unitAmountCurrency || (unitAmountTaxCodes && unitAmountTaxCodes.length))) {
+      throw new Error("Provide either Unit Amount - Gross Value or Unit Amount - Net Value when specifying currency or tax codes.");
+    }
🧹 Nitpick comments (16)
components/mews/actions/create-order/create-order.mjs (1)

141-149: Optional: avoid sending undefined for optional fields

If the HTTP layer doesn’t strip undefined, API may reject payload. The diff above already applies conditional spreads.

components/mews/actions/fetch-customers/fetch-customers.mjs (3)

77-92: Tighten email picker mapping for clarity and resilience

Current labels omit the email; values may be undefined for customers without email. Improve discoverability and reduce ambiguity.

       propDefinition: [
         app,
         "customerId",
         () => ({
-          mapper: (customer) => ({
-            label: `${customer.FirstName} ${customer.LastName}`,
-            value: customer.Email,
-          }),
+          mapper: (customer) => ({
+            // Include email in the label for clarity and disambiguation
+            label: `${customer.FirstName ?? ""} ${customer.LastName ?? ""} — ${customer.Email ?? ""}`.trim(),
+            value: customer.Email,
+          }),
         }),
       ],

121-137: Auto-include 'Deleted' in ActivityStates when DeletedUtc filters are used; enforce single CompanyId

Align behavior with your own prop docs (“ActivityStates value 'Deleted' should be provided…”, “Max 1 item”), reducing surprising results and API errors.

   async run({ $ }) {
     const {
       app,
       createdStartUtc,
       createdEndUtc,
       updatedStartUtc,
       updatedEndUtc,
       deletedStartUtc,
       deletedEndUtc,
       activityStates,
       customerIds,
       companyIds,
       emails,
       firstNames,
       lastNames,
       extentCustomers,
       extentAddresses,
     } = this;
 
+    // If DeletedUtc is used but 'Deleted' isn't in ActivityStates, auto-include it.
+    const deletedAwareActivityStates = (deletedStartUtc || deletedEndUtc)
+      ? (activityStates?.includes("Deleted")
+        ? activityStates
+        : [ ...(activityStates || []), "Deleted" ])
+      : activityStates;
+
+    // Enforce API note: CompanyIds max 1 item
+    if (companyIds?.length > 1) {
+      throw new Error("Provide at most 1 Company ID as per Mews API.");
+    }

162-172: Wire the derived ActivityStates into the payload

Use the computed deletedAwareActivityStates when building the request.

           Extent: {
             Customers: extentCustomers,
             Addresses: extentAddresses,
           },
-          ActivityStates: activityStates,
+          ActivityStates: deletedAwareActivityStates,
           CustomerIds: customerIds,
           CompanyIds: companyIds,
           Emails: emails,
           FirstNames: firstNames,
           LastNames: lastNames,
components/mews/actions/create-task/create-task.mjs (1)

52-61: Only send Description when provided

Keep payloads minimal; avoid sending undefined.

     const response = await app.tasksCreate({
       $,
       data: {
         Name: name,
-        Description: description,
+        ...(description && { Description: description }),
         DepartmentId: departmentId,
         ServiceOrderId: serviceOrderId,
         DeadlineUtc: deadlineUtc,
       },
     });
components/mews/actions/get-bill-pdf/get-bill-pdf.mjs (1)

24-51: Optional: provide a default PDF template

Setting a sensible default improves UX; “Detailed” is typically expected.

     pdfTemplate: {
       type: "string",
       label: "PDF Template",
       description: "Bill PDF template type. If not specified, the default template is used.",
-      optional: true,
+      optional: true,
+      default: "Detailed",
       options: [
components/mews/actions/fetch-order-items/fetch-order-items.mjs (2)

148-170: Guard against unbounded queries by requiring at least one filter

The API typically expects some filter; failing to set one can be slow/heavy. Fail-fast with a clear message.

   async run({ $ }) {
     const {
       app,
       createdStartUtc,
       createdEndUtc,
       updatedStartUtc,
       updatedEndUtc,
       consumedStartUtc,
       consumedEndUtc,
       canceledStartUtc,
       canceledEndUtc,
       closedStartUtc,
       closedEndUtc,
       enterpriseIds,
       orderItemIds,
       accountIds,
       serviceOrderIds,
       serviceIds,
       billIds,
       currency,
       accountingStates,
       types,
     } = this;
 
+    const hasAnyFilter = Boolean(
+      createdStartUtc || createdEndUtc ||
+      updatedStartUtc || updatedEndUtc ||
+      consumedStartUtc || consumedEndUtc ||
+      canceledStartUtc || canceledEndUtc ||
+      closedStartUtc || closedEndUtc ||
+      enterpriseIds?.length || orderItemIds?.length || accountIds?.length ||
+      serviceOrderIds?.length || serviceIds?.length || billIds?.length ||
+      currency || accountingStates?.length || types?.length
+    );
+    if (!hasAnyFilter) {
+      throw new Error("Provide at least one filter (e.g., time range, IDs, or states) to fetch order items.");
+    }

177-206: Reduce duplication when building date interval objects

This pattern is repeated across actions. Consider a small helper (e.g., buildInterval(start, end, fieldName)) in common utils to improve maintainability.

I can extract a shared helper and update all fetch actions in this PR for consistency. Want me to follow up with a patch?

components/mews/actions/update-reservation/update-reservation.mjs (4)

37-42: Fix doc/type mismatch for Value-wrapped fields (users should enter raw values).

These props are typed as string/boolean, and the run() method correctly wraps them as { Value }. However, the descriptions instruct users to "Pass an object with 'Value' property or null", which is misleading for the component API.

Recommend updating the descriptions to reflect that users should supply raw values and leave blank to keep unchanged. If “clear/unset” is needed, that’s currently not supported (see clearing comment below).

Apply this pattern (example diffs shown for a few props; repeat similarly for the rest):

-      description: "Number of the reservation within the Channel (i.e. OTA, GDS, CRS, etc) in case the reservation group originates there (e.g. Booking.com confirmation number). Pass an object with 'Value' property or null if the channel number should not be updated.",
+      description: "Number of the reservation within the Channel (e.g. Booking.com confirmation number). Provide the value to set; leave blank to keep unchanged.",
-      description: "Reservation start in UTC timezone in ISO 8601 format. Pass an object with 'Value' property or null if the start time should not be updated.",
+      description: "Reservation start in UTC timezone in ISO 8601 format. Provide the value to set; leave blank to keep unchanged.",
-      description: "Reservation end in UTC timezone in ISO 8601 format. Pass an object with 'Value' property or null if the end time should not be updated.",
+      description: "Reservation end in UTC timezone in ISO 8601 format. Provide the value to set; leave blank to keep unchanged.",
-      description: "Date when the optional reservation is released in UTC timezone in ISO 8601 format. Pass an object with 'Value' property or null if the release time should not be updated.",
+      description: "Date when the optional reservation is released in UTC timezone in ISO 8601 format. Provide the value to set; leave blank to keep unchanged.",
-      description: "Identifier of the assigned Resource. If the assigned resource is locked, see AssignedResourceLocked for updating the assigned resource. Pass an object with 'Value' property or null if the assigned resource should not be updated.",
+      description: "Identifier of the assigned Resource. If the assigned resource is locked, see AssignedResourceLocked. Provide the value to set; leave blank to keep unchanged.",
-      description: "Identifier of the requested ResourceCategory. Pass an object with 'Value' property or null if resource category should not be updated.",
+      description: "Identifier of the requested ResourceCategory. Provide the value to set; leave blank to keep unchanged.",
-      description: "Purpose of the reservation. Pass an object with 'Value' property (Business, Leisure, Student) or null if the purpose should not be updated.",
+      description: "Purpose of the reservation. One of: Business, Leisure, Student. Provide the value to set; leave blank to keep unchanged.",
-      description: "Identifier of the Customer on whose behalf the reservation was made. Pass an object with 'Value' property or null if the booker should not be updated.",
+      description: "Identifier of the Customer on whose behalf the reservation was made. Provide the value to set; leave blank to keep unchanged.",
-      description: "Whether the reservation should be locked to the assigned Resource. To reassign the reservation to a new Resource, first set AssignedResourceLocked to false to unlock the resource. Then, assign the reservation to a new Resource by setting AssignedResourceId to the new resource ID. Pass an object with 'Value' property (boolean) or null if the lock should not be updated.",
+      description: "Whether the reservation should be locked to the assigned Resource. To reassign, first set AssignedResourceLocked to false, then set AssignedResourceId to the new resource ID. Provide the boolean value to set; leave blank to keep unchanged.",

Also applies to: 43-48, 49-54, 55-60, 85-90, 91-96, 119-124, 173-180, 181-186


61-84: Clarify input format for string[] props that expect object arrays.

Both personCounts and timeUnitPrices are typed as string[], but the descriptions/examples show arrays of objects. If the intention is “one JSON object per item”, please clarify that each item in the array should be a JSON string representing the object (parsed via utils.parseArray at runtime).

For example:

-      description: `Number of people per age category the reservation is for. If supplied, the person counts will be replaced. Each item should contain:
+      description: `Number of people per age category the reservation is for. If supplied, the person counts will be replaced.
+
+Provide each item as a JSON string representing an object with:
   - \`AgeCategoryId\` (string, required): Unique identifier of the Age category
   - \`Count\` (integer, required): Number of people of a given age category. Only positive value is accepted

And similarly for Time Unit Prices (note at the top that each array item is a JSON string representing the object shown in the example).

Also applies to: 137-171


200-225: Cannot clear/unset fields that use { Value } wrappers (no way to send Value: null).

Because fields are included only when truthy (e.g. (channelNumber && {...})), users can update a value but can’t explicitly clear it (send { Value: null }). If the API supports clearing, expose a way to do it.

Two lightweight patterns:

  • Add paired boolean “clear” toggles (e.g. clearChannelNumber). If true, send { Value: null } and ignore the input value.
  • Add a generic string[] prop, e.g. clearFields, and map it to those wrappers dynamically.

Example pattern for ChannelNumber (apply similarly to other wrappers):

   props: {
     app,
+    clearChannelNumber: {
+      type: "boolean",
+      label: "Clear Channel Number",
+      description: "If true, clears the Channel Number (sends { Value: null }).",
+      optional: true,
+    },
     channelNumber: {
       type: "string",
       label: "Channel Number",
-      description: "Number of the reservation within the Channel (i.e. OTA, GDS, CRS, etc) in case the reservation group originates there (e.g. Booking.com confirmation number). Pass an object with 'Value' property or null if the channel number should not be updated.",
+      description: "Number of the reservation within the Channel (e.g., Booking.com confirmation number).",
       optional: true,
     },
-            ...(channelNumber && {
-              ChannelNumber: {
-                Value: channelNumber,
-              },
-            }),
+            ...((this.clearChannelNumber || channelNumber) && {
+              ChannelNumber: {
+                Value: this.clearChannelNumber ? null : channelNumber,
+              },
+            }),

If you prefer the generic list approach, I can draft that refactor.

Also applies to: 236-327


12-23: Reason should be required when changing price-related fields (guard before API call).

Per description, Reason is required when updating the price. That typically includes reprice=true, changing RateId, or providing TimeUnitPrices. Add a minimal validation before calling the API.

   async run({ $ }) {
     const {
       app,
       reason,
       reprice,
       applyCancellationFee,
       reservationId,
       channelNumber,
       startUtc,
       endUtc,
       releasedUtc,
       personCounts,
       assignedResourceId,
       requestedCategoryId,
       travelAgencyId,
       companyId,
       businessSegmentId,
       purpose,
-      rateId,
+      rateId,
       creditCardId,
       timeUnitPrices,
       bookerId,
       assignedResourceLocked,
       availabilityBlockId,
       optionsOwnerCheckedIn,
     } = this;

+    // Minimal validation for price updates
+    const isPriceUpdate =
+      reprice === true
+      || !!rateId
+      || (Array.isArray(timeUnitPrices) && timeUnitPrices.length > 0);
+    if (isPriceUpdate && !reason) {
+      throw new Error("Reason is required when updating reservation pricing (reprice, rateId, or timeUnitPrices).");
+    }

Please confirm against Mews’ latest docs whether changing RateId should also require a Reason.

Also applies to: 219-225

components/mews/mews.app.mjs (1)

137-142: Optional: add options loader for Travel Agency.

Currently travelAgencyId is free-form with no options. If Travel Agencies are represented among Companies in your API scope, consider providing options via companiesGetAll() for parity with companyId.

Example:

     travelAgencyId: {
       type: "string",
       label: "Travel Agency ID",
       description: "Identifier of the Travel Agency.",
       optional: true,
+      async options() {
+        const { Companies: companies } = await this.companiesGetAll();
+        return companies.map(({ Name: label, Id: value }) => ({ label, value }));
+      },
     },

If a narrower endpoint/filter exists for Travel Agencies, that’d be preferred—let me know and I can adjust the mapping.

components/mews/actions/add-reservation-product/add-reservation-product.mjs (1)

88-103: Only include UnitAmount if any of its fields are provided.

If none of the UnitAmount fields are set, omit the object entirely to avoid sending empty payloads.

-    const response = await app.reservationsAddProduct({
+    const anyUnitAmount =
+      unitAmountGrossValue !== undefined && unitAmountGrossValue !== null && unitAmountGrossValue !== ""
+      || unitAmountNetValue !== undefined && unitAmountNetValue !== null && unitAmountNetValue !== ""
+      || !!unitAmountCurrency
+      || (Array.isArray(unitAmountTaxCodes) && unitAmountTaxCodes.length > 0);
+    const response = await app.reservationsAddProduct({
       $,
       data: {
         ReservationId: reservationId,
         ProductId: productId,
         StartUtc: startUtc,
         EndUtc: endUtc,
         Count: count,
-        UnitAmount: {
-          Currency: unitAmountCurrency,
-          GrossValue: unitAmountGrossValue,
-          NetValue: unitAmountNetValue,
-          TaxCodes: unitAmountTaxCodes,
-        },
+        ...(anyUnitAmount && {
+          UnitAmount: {
+            Currency: unitAmountCurrency,
+            GrossValue: unitAmountGrossValue,
+            NetValue: unitAmountNetValue,
+            TaxCodes: unitAmountTaxCodes,
+          },
+        }),
       },
     });
components/mews/actions/fetch-products/fetch-products.mjs (2)

56-64: Honor described default for Include Default.

The description states: if Product IDs are provided, Include Default defaults to true; otherwise false. Implement that computed default.

   async run({ $ }) {
     const {
       app,
       serviceIds,
       updatedStartUtc,
       updatedEndUtc,
       enterpriseIds,
       productIds,
-      includeDefault,
+      includeDefault,
     } = this;

+    const includeDefaultComputed =
+      this.includeDefault ?? Boolean(Array.isArray(productIds) && productIds.length);
+
     const items = await app.paginate({
       requester: app.productsGetAll,
       requesterArgs: {
         $,
         data: {
           ServiceIds: serviceIds,
           ...(updatedStartUtc || updatedEndUtc) && {
             UpdatedUtc: {
               StartUtc: updatedStartUtc,
               EndUtc: updatedEndUtc,
             },
           },
           EnterpriseIds: enterpriseIds,
           ProductIds: productIds,
-          IncludeDefault: includeDefault,
+          IncludeDefault: includeDefaultComputed,
         },
       },
       resultKey: "Products",
     });

Also applies to: 66-84


20-31: Optional: Validate the UpdatedUtc interval (max 3 months).

You note a 3-month max interval; consider a lightweight check to fail fast with a helpful error if the range exceeds this, rather than letting the API reject it.

I can add a small date-diff validation if desired.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 74feaff and 37d3820.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (28)
  • components/mews/actions/add-customer-file/add-customer-file.mjs (1 hunks)
  • components/mews/actions/add-reservation-companion/add-reservation-companion.mjs (1 hunks)
  • components/mews/actions/add-reservation-product/add-reservation-product.mjs (1 hunks)
  • components/mews/actions/cancel-reservation/cancel-reservation.mjs (1 hunks)
  • components/mews/actions/create-availability-block/create-availability-block.mjs (1 hunks)
  • components/mews/actions/create-order/create-order.mjs (1 hunks)
  • components/mews/actions/create-reservation/create-reservation.mjs (1 hunks)
  • components/mews/actions/create-task/create-task.mjs (1 hunks)
  • components/mews/actions/fetch-customers/fetch-customers.mjs (1 hunks)
  • components/mews/actions/fetch-order-items/fetch-order-items.mjs (1 hunks)
  • components/mews/actions/fetch-products/fetch-products.mjs (1 hunks)
  • components/mews/actions/fetch-reservations/fetch-reservations.mjs (1 hunks)
  • components/mews/actions/get-bill-pdf/get-bill-pdf.mjs (1 hunks)
  • components/mews/actions/get-rate-prices/get-rate-prices.mjs (1 hunks)
  • components/mews/actions/update-customer/update-customer.mjs (1 hunks)
  • components/mews/actions/update-reservation/update-reservation.mjs (1 hunks)
  • components/mews/common/utils.mjs (1 hunks)
  • components/mews/mews.app.mjs (9 hunks)
  • components/mews/package.json (1 hunks)
  • components/mews/sources/bill-closed/bill-closed.mjs (1 hunks)
  • components/mews/sources/company-created/company-created.mjs (1 hunks)
  • components/mews/sources/customer-created/customer-created.mjs (1 hunks)
  • components/mews/sources/order-item-created/order-item-created.mjs (1 hunks)
  • components/mews/sources/order-item-updated/order-item-updated.mjs (1 hunks)
  • components/mews/sources/product-service-order-created/product-service-order-created.mjs (1 hunks)
  • components/mews/sources/reservation-cancelled/reservation-cancelled.mjs (1 hunks)
  • components/mews/sources/reservation-created/reservation-created.mjs (1 hunks)
  • components/mews/sources/reservation-updated/reservation-updated.mjs (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • components/mews/sources/order-item-created/order-item-created.mjs
  • components/mews/sources/reservation-updated/reservation-updated.mjs
🚧 Files skipped from review as they are similar to previous changes (17)
  • components/mews/sources/reservation-created/reservation-created.mjs
  • components/mews/sources/product-service-order-created/product-service-order-created.mjs
  • components/mews/sources/reservation-cancelled/reservation-cancelled.mjs
  • components/mews/sources/customer-created/customer-created.mjs
  • components/mews/actions/create-availability-block/create-availability-block.mjs
  • components/mews/sources/company-created/company-created.mjs
  • components/mews/actions/add-customer-file/add-customer-file.mjs
  • components/mews/actions/cancel-reservation/cancel-reservation.mjs
  • components/mews/actions/create-reservation/create-reservation.mjs
  • components/mews/actions/get-rate-prices/get-rate-prices.mjs
  • components/mews/package.json
  • components/mews/sources/order-item-updated/order-item-updated.mjs
  • components/mews/sources/bill-closed/bill-closed.mjs
  • components/mews/common/utils.mjs
  • components/mews/actions/fetch-reservations/fetch-reservations.mjs
  • components/mews/actions/add-reservation-companion/add-reservation-companion.mjs
  • components/mews/actions/update-customer/update-customer.mjs
🧰 Additional context used
🧬 Code Graph Analysis (8)
components/mews/actions/create-task/create-task.mjs (2)
components/mews/actions/add-customer-file/add-customer-file.mjs (1)
  • response (50-58)
components/mews/actions/create-reservation/create-reservation.mjs (1)
  • response (118-157)
components/mews/actions/create-order/create-order.mjs (2)
components/mews/actions/add-reservation-product/add-reservation-product.mjs (1)
  • response (88-103)
components/mews/actions/update-reservation/update-reservation.mjs (1)
  • response (227-331)
components/mews/actions/get-bill-pdf/get-bill-pdf.mjs (1)
components/mews/mews.app.mjs (1)
  • response (551-560)
components/mews/actions/fetch-order-items/fetch-order-items.mjs (4)
components/mews/actions/fetch-products/fetch-products.mjs (1)
  • items (66-84)
components/mews/actions/fetch-customers/fetch-customers.mjs (1)
  • items (139-175)
components/mews/actions/fetch-reservations/fetch-reservations.mjs (1)
  • items (228-290)
components/mews/sources/common/polling.mjs (1)
  • items (97-109)
components/mews/actions/add-reservation-product/add-reservation-product.mjs (1)
components/mews/actions/add-reservation-companion/add-reservation-companion.mjs (1)
  • response (33-39)
components/mews/actions/fetch-products/fetch-products.mjs (4)
components/mews/actions/fetch-order-items/fetch-order-items.mjs (1)
  • items (172-219)
components/mews/actions/fetch-customers/fetch-customers.mjs (1)
  • items (139-175)
components/mews/actions/fetch-reservations/fetch-reservations.mjs (1)
  • items (228-290)
components/mews/sources/common/polling.mjs (1)
  • items (97-109)
components/mews/actions/fetch-customers/fetch-customers.mjs (4)
components/mews/actions/fetch-order-items/fetch-order-items.mjs (1)
  • items (172-219)
components/mews/actions/fetch-products/fetch-products.mjs (1)
  • items (66-84)
components/mews/actions/fetch-reservations/fetch-reservations.mjs (1)
  • items (228-290)
components/mews/sources/common/polling.mjs (1)
  • items (97-109)
components/mews/actions/update-reservation/update-reservation.mjs (2)
components/mews/actions/create-reservation/create-reservation.mjs (1)
  • response (118-157)
components/mews/actions/update-customer/update-customer.mjs (1)
  • response (244-279)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Lint Code Base
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (7)
components/mews/actions/fetch-customers/fetch-customers.mjs (1)

177-179: LGTM on summary formatting

Good pluralization handling.

components/mews/actions/create-task/create-task.mjs (1)

1-41: Solid new action

Props and mappings align with Mews “Add task” API; good use of propDefinitions for Department and Service Order.

components/mews/actions/get-bill-pdf/get-bill-pdf.mjs (1)

68-80: Confirm binary/base64 handling for PDF response

If billsGetPdf returns base64 content and filename, ensure the response is shaped for downstream usage (e.g., expose { fileName, contentBase64 }) and that the HTTP client doesn’t try to JSON-parse binary.

If needed, consider:

-    const response = await app.billsGetPdf({
+    const response = await app.billsGetPdf({
       $,
       data: {
         BillId: billId,
         BillPrintEventId: billPrintEventId,
         PdfTemplate: pdfTemplate,
         PrintReason: printReason,
       },
     });
 
-    $.export("summary", `Successfully retrieved PDF for bill ${billId}`);
-    return response;
+    $.export("summary", `Successfully retrieved PDF for bill ${billId}`);
+    // If app.billsGetPdf returns { FileName, Data } (base64), return a structured object
+    return {
+      billId,
+      fileName: response?.FileName ?? `bill-${billId}.pdf`,
+      contentBase64: response?.Data ?? response, // adjust to actual shape
+    };
components/mews/actions/fetch-order-items/fetch-order-items.mjs (1)

221-223: LGTM on summary formatting

Good pluralization handling.

components/mews/actions/update-reservation/update-reservation.mjs (1)

311-327: Good boolean gating for optional flags.

Using !== undefined ensures false is preserved while omitting truly unset values. Same for nested Options.OwnerCheckedIn. Nicely done.

components/mews/mews.app.mjs (1)

511-516: New endpoints look consistent with Mews Connector API paths.

billsGetPdf, countriesGetAll, creditCardsGetAll, and availabilityBlocksGetAll methods follow the expected path patterns and composition with _makeRequest.

Also applies to: 523-534

components/mews/actions/add-reservation-product/add-reservation-product.mjs (1)

45-56: Confirm currency requirement when overriding price.

If a custom price is provided (Gross/Net), does the API require Currency to be present? If yes, make unitAmountCurrency required only when one of the amounts is provided and validate accordingly.

I can add conditional validation (and mark the prop required dynamically) once you confirm the API requirement.

Also applies to: 63-72

@jcortes jcortes force-pushed the mews-new-component-second-part branch from 37d3820 to cfa444a Compare August 20, 2025 19:51
@jcortes jcortes requested a review from michelle0927 August 20, 2025 19:51
@jcortes jcortes force-pushed the mews-new-component-second-part branch from cfa444a to 12da217 Compare August 20, 2025 20:30
@jcortes jcortes requested a review from michelle0927 August 20, 2025 20:30
michelle0927
michelle0927 previously approved these changes Aug 20, 2025
Copy link
Collaborator

@michelle0927 michelle0927 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@jcortes jcortes force-pushed the mews-new-component-second-part branch 3 times, most recently from 669b02f to 86c12e9 Compare August 26, 2025 16:18
@jcortes jcortes force-pushed the mews-new-component-second-part branch from 86c12e9 to 909db14 Compare August 27, 2025 14:37
@jcortes
Copy link
Collaborator Author

jcortes commented Aug 27, 2025

/approve

@jcortes jcortes merged commit 8c66e63 into master Aug 27, 2025
10 checks passed
@jcortes jcortes deleted the mews-new-component-second-part branch August 27, 2025 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mews

3 participants