Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Comments

fix: pos productgroup#6513

Merged
batorshih merged 12 commits intoerxes:mainfrom
batorshih:main_copy
Oct 21, 2025
Merged

fix: pos productgroup#6513
batorshih merged 12 commits intoerxes:mainfrom
batorshih:main_copy

Conversation

@batorshih
Copy link
Member

@batorshih batorshih commented Oct 21, 2025

Summary by Sourcery

Support product group management within POS creation and editing flows by extending types, form schema, and UI, and persist groups via a dedicated GraphQL mutation. Also enforce default onServer flags and streamline TRPC crudData behavior.

New Features:

  • Add support for defining product groups in POS create/edit, including include/exclude categories and products
  • Introduce usePosEditProductGroup hook and integrate productGroupsBulkInsert mutation to sync product groups separately
  • Add toast notification for successful POS edits

Bug Fixes:

  • Ensure onServer flag is always set for new and edited POS records
  • Correct TRPC posclient crudData route to use mutation signature

Enhancements:

  • Refactor create-pos types and form schema to use array-based productGroups with optional fields and normalize naming
  • Enhance ProductForm UI to manage product groups and mapping rules with updated inputs, labels, and default values
  • Update backend GraphQL resolvers to default onServer flag to true for posAdd/posEdit
  • Consolidate TRPC posclient crudData into a mutation and adjust sendPosclientMessage to dynamically select query or mutation

Important

Enhance POS product group management with backend mutations and frontend UI updates for CRUD operations.

  • Backend Changes:
    • In posclient.ts, change crudData from query to mutation to handle data modifications.
    • Update sendPosclientMessage in initWorker.ts to use mutation for crudData actions.
    • Add productGroupsBulkInsert mutation in pos.ts to handle bulk insert and update of product groups.
  • Frontend Changes:
    • Add POS_EDITED message to TOAST_MESSAGES in constants/index.tsx.
    • Update formSchema.ts to include productGroups in productSchema.
    • Modify ProductForm in product.tsx to handle product group fields and interactions.
    • Implement usePosEditProductGroup hook in usePosEditProductGroup.tsx for product group mutations.
    • Update posDetail.tsx to integrate product group editing and saving logic.

This description was created by Ellipsis for c859769. You can customize this summary. It will automatically update as commits are pushed.

Summary by CodeRabbit

  • New Features

    • Product group management now available for POS configuration with category and product exclusion options.
    • Product mapping fields (name, code) added for detailed product management.
    • Success notification added when POS is saved.
  • Refactor

    • Data modification operations now correctly use mutation-based API calls instead of queries.

@sourcery-ai
Copy link

sourcery-ai bot commented Oct 21, 2025

Reviewer's Guide

This PR implements full support for POS product groups by extending types and schemas, enhancing the product form UI, integrating new hooks in the PosEdit flow, updating backend GraphQL mutations, fixing a TRPC method, and normalizing imports and formatting across modules.

Sequence diagram for POS product group save flow in PosEdit

sequenceDiagram
  participant User
  participant PosEdit
  participant usePosEditProductGroup
  participant Backend
  User->>PosEdit: Clicks Save on POS Edit
  PosEdit->>usePosEditProductGroup: Calls productGroupSave(finalDataProductGroup)
  usePosEditProductGroup->>Backend: GraphQL mutation productGroupsBulkInsert
  Backend-->>usePosEditProductGroup: Returns updated product groups
  usePosEditProductGroup-->>PosEdit: Refetch product groups
  PosEdit-->>User: Shows toast 'POS edited successfully'
Loading

Class diagram for updated ProductGroup and ProductServiceSettings types

classDiagram
  class ProductGroup {
    +string name
    +string[] categoryIds
    +string[] excludedCategoryIds
    +string[] excludedProductIds
    +string description
  }
  class ProductServiceSettings {
    +ProductGroup[] productGroups
    +string initialProductCategory
    +string kioskExcludeCategories
    +string kioskExcludeProducts
    +boolean remainderConfigEnabled
    +string excludeCategories
    +boolean banFractions
  }
  ProductServiceSettings "1" o-- "*" ProductGroup
Loading

Class diagram for updated ProductFormValues and related schemas

classDiagram
  class ProductFormValues {
    +string[] productDetails
    +CatProdMapping[] catProdMappings
    +string[] initialCategoryIds
    +string[] kioskExcludeCategoryIds
    +string[] kioskExcludeProductIds
    +string[] checkExcludeCategoryIds
    +ProductGroup[] productGroups
  }
  class CatProdMapping {
    +string _id
    +string categoryId
    +string productId
    +string name
    +string code
  }
  class ProductGroup {
    +string name
    +string[] categoryIds
    +string[] excludedCategoryIds
    +string[] excludedProductIds
    +string description
    +boolean isRequired
  }
  ProductFormValues "1" o-- "*" CatProdMapping
  ProductFormValues "1" o-- "*" ProductGroup
Loading

File-Level Changes

Change Details Files
Refactor and extend ProductGroup types and schemas
  • Updated ProductGroup interface to use optional category and product ID arrays with appropriate naming and optional descriptions
  • Extended productSchema and catProdMappingSchema in formSchema to include productGroups and renamed mapping fields (_id, name, code, productId)
  • Normalized interfaces for ProductServiceSettings, CashierSettings, etc., and added semicolons/trailing commas
frontend/plugins/sales_ui/src/modules/pos/create-pos/types/index.ts
frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts
Enhance ProductForm component to handle productGroups
  • Added productGroups prop and watch logic to useEffect, replaced productDetails with productGroups in add/remove flows
  • Renamed form fields and UI labels for included/excluded categories and products, introduced multi-select and input fields
  • Updated button logic and mapping rendering to align with new grouping structure
frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx
Integrate product group hooks and persistence in PosEdit
  • Introduced usePosEditProductGroup hook with save mutation and cache update logic
  • Fetched productGroups via useProductGroups, passed into ProductForm and productForm hook
  • After posEdit mutation, triggered productGroupSave with formatted groups and displayed POS_EDITED toast
frontend/plugins/sales_ui/src/modules/pos/pos-detail/components/posDetail.tsx
frontend/plugins/sales_ui/src/modules/pos/hooks/usePosEditProductGroup.tsx
frontend/plugins/sales_ui/src/modules/pos/pos-detail/hooks/useProductGroup.ts
frontend/plugins/sales_ui/src/modules/pos/constants/index.tsx
Update backend GraphQL pos mutations for productGroups and onServer flag
  • Enforced doc.onServer = true by default in posAdd and posEdit resolvers
  • Adjusted productGroupsBulkInsert mutation to handle new group fields and maintain existing ones
  • Fixed trailing comma formatting and upsert option in posSlotBulkUpdate
backend/plugins/sales_api/src/modules/pos/graphql/resolvers/mutations/pos.ts
Fix TRPC crudData to use mutation and simplify input
  • Changed crudData procedure from query to mutation and adjusted input handling
  • Ensured consistent input destructuring for configs and crudData
backend/plugins/posclient_api/src/modules/posclient/trpc/posclient.ts
Misc formatting, import normalization, and utility tweaks
  • Normalized import paths and quotes, added missing trailing commas and semicolons
  • Updated initWorker sendPosclientMessage to separate input token and method logic
  • Fixed setFormStepData formatting and added default POS_EDITED toast constant
backend/plugins/sales_api/src/initWorker.ts
frontend/plugins/sales_ui/src/modules/pos/create-pos/hooks/usePosCreateForm.tsx
frontend/plugins/sales_ui/src/modules/pos/constants/index.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

Walkthrough

This pull request refactors POS product management by introducing product groups as a distinct entity. Backend procedures are updated to use mutations for data modifications, and frontend types, components, and hooks are restructured to support the new product group workflow with corresponding UI updates.

Changes

Cohort / File(s) Summary
Backend TRPC & Worker Updates
backend/plugins/posclient_api/src/modules/posclient/trpc/posclient.ts, backend/plugins/sales_api/src/initWorker.ts
TRPC crudData procedure converted from query to mutation; manage input handling simplified; worker initialization refactored to use two-step TRPC call with dynamic method selection (mutation for crudData, query otherwise) and flat input payloads.
Backend Resolver Formatting
backend/plugins/sales_api/src/modules/pos/graphql/resolvers/mutations/pos.ts
Multiple resolver method signatures updated with trailing commas in destructured parameters; default onServer behavior added to posAdd and posEdit flows; minor punctuation and formatting adjustments across posAdd, posEdit, posRemove, productGroupsBulkInsert, posSlotBulkUpdate.
Frontend Constants
frontend/plugins/sales_ui/src/modules/pos/constants/index.tsx
New toast message key POS_EDITED added to TOAST_MESSAGES constant.
Frontend Form Schema
frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts
productDetails field type changed from array of objects to array of strings; new productGroups field added as array of productDetailSchema; optional _id field added to catProdMappingSchema.
Frontend Type Definitions
frontend/plugins/sales_ui/src/modules/pos/create-pos/types/index.ts
ProductGroup interface restructured with optional categoryIds, excludedCategoryIds, excludedProductIds fields; new interfaces added: SyncCardConfig, SyncCardSettings, DeliveryConfigSettings, EbarimtConfigSettings, FinanceConfigSettings, Branch, Department; PaymentMethod formatting adjusted.
Frontend Product Component
frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx
New productGroups prop added to component; form logic and UI sections updated to use productGroups instead of productDetails; field mappings restructured with new catProdMappings handling; UI labels and helper text updated; debug logging added.
Frontend Pos Detail Component
frontend/plugins/sales_ui/src/modules/pos/pos-detail/components/posDetail.tsx
Product-related editing flow integrated with productForm initialization and product group payload handling; productGroupSave mutation triggered on successful posEdit; new imports and hooks wired (usePosEditProductGroup, useProductGroups, useToast); POS_EDITED toast notification added.
Frontend New Product Group Hook
frontend/plugins/sales_ui/src/modules/pos/hooks/usePosEditProductGroup.tsx
New React hook usePosEditProductGroup introduced to manage GraphQL mutation for saving product groups with cache update handling.
Frontend Form Hooks & Cleanup
frontend/plugins/sales_ui/src/modules/pos/create-pos/hooks/usePosCreateForm.tsx
Minor formatting updates: trailing commas added to imports and function calls; arrow function parameter style adjusted.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Frontend as Frontend (POS Detail)
    participant Mutation as Backend (posEdit Mutation)
    participant ProductGroup as Backend (productGroupsBulkInsert)
    participant Cache as Apollo Cache

    User->>Frontend: Submit POS edit with product groups
    Frontend->>Mutation: Call posEdit mutation
    Mutation-->>Frontend: posEdit response
    Frontend->>ProductGroup: Call productGroupSave (productGroupsBulkInsert)
    ProductGroup-->>Frontend: Product groups saved, data.productGroupsBulkInsert returned
    Frontend->>Cache: Update cache with fieldsToUpdate map
    Cache-->>Frontend: Cache updated
    Frontend->>Frontend: Show POS_EDITED toast
    Frontend->>User: Success notification
Loading
sequenceDiagram
    participant Worker as Worker (initWorker)
    participant Input as Input Handler
    participant TRPC as TRPC Method Router
    participant Backend as Backend Procedure

    Worker->>Input: Receive operation (lastAction, data)
    Input->>Input: Build local input object
    alt lastAction === 'crudData'
        Input->>TRPC: Route to mutation
    else
        Input->>TRPC: Route to query
    end
    TRPC->>Backend: Call selected procedure
    Backend-->>Worker: Return result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

The changes span multiple layers (backend procedures, worker logic, frontend types, components, and hooks) with diverse modifications including procedure type conversions, significant form refactoring, new type definitions, and product group workflow integration. While many individual changes are straightforward formatting or schema adjustments, the interconnected nature of product group introduction across backend and frontend, combined with form logic restructuring and new hook wiring, requires careful verification of type consistency, data flow, and UI/mutation coordination.

Suggested reviewers

  • munkhsaikhan
  • Enkhtuvshin0513

Poem

🐰 A hop through product groups we go,
Where forms now flow and schemas grow!
Mutations dance where queries played,
New toasts appear when saves are made—
ProductGroups bloom, the POS refined! 🌱

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title "fix: pos productgroup" is related to the changeset but lacks sufficient clarity and specificity. While it correctly identifies the general areas of change (POS and productgroup), it does not adequately convey the primary scope of the changeset. The modifications represent a significant refactoring that restructures product management from productDetails to productGroups, converts TRPC procedures from queries to mutations, and integrates new product group saving workflows. Additionally, the use of "fix:" is potentially misleading, as the changes appear to be more of a feature enhancement or architectural refactoring rather than a targeted bug fix. A reviewer scanning commit history would struggle to quickly understand the nature and scope of these changes from the title alone. Consider revising the title to be more descriptive and specific about the actual changes, such as "Refactor POS product management to use productGroups instead of productDetails" or "Implement productGroups system with TRPC mutation support for POS editing." This would provide clearer context to developers reviewing commit history and better reflect the significant architectural changes introduced in this PR.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • In sendPosclientMessage, the code references an undefined variable 'data'—it should use 'args.data' (or properly destructure) to prevent runtime failures.
  • The 'POS edited' toast uses a 'destructive' variant which suggests an error; consider switching to a success or neutral variant for clarity.
  • usePosEditProductGroup is invoked with an empty fields array, so cache.modify will never run; either pass the intended fields or simplify the cache update logic.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In sendPosclientMessage, the code references an undefined variable 'data'—it should use 'args.data' (or properly destructure) to prevent runtime failures.
- The 'POS edited' toast uses a 'destructive' variant which suggests an error; consider switching to a success or neutral variant for clarity.
- usePosEditProductGroup is invoked with an empty fields array, so cache.modify will never run; either pass the intended fields or simplify the cache update logic.

## Individual Comments

### Comment 1
<location> `frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx:47` </location>
<code_context>
   useEffect(() => {
     if (!posDetail) return;
-
+    console.log('productGroups', productGroups);
     form.reset({
       productDetails: posDetail.productDetails || [],
</code_context>

<issue_to_address>
**suggestion:** Remove console.log statement before merging.

Console.log statements should be removed from production code to avoid unnecessary log output and possible exposure of sensitive data.

```suggestion
```
</issue_to_address>

### Comment 2
<location> `backend/plugins/sales_api/src/initWorker.ts:66-74` </location>
<code_context>
  const ret = await sendTRPCMessage({
    pluginName: 'posclient',
    method: lastAction === 'crudData' ? 'mutation' : 'query',
    module: 'posclient',
    action: lastAction,
    input: { ...input },
    defaultValue: {},
  });
  return ret;

</code_context>

<issue_to_address>
**suggestion (code-quality):** Inline variable that is immediately returned ([`inline-immediately-returned-variable`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/TypeScript/Default-Rules/inline-immediately-returned-variable))

```suggestion
  return await sendTRPCMessage({
      pluginName: 'posclient',
      method: lastAction === 'crudData' ? 'mutation' : 'query',
      module: 'posclient',
      action: lastAction,
      input: { ...input },
      defaultValue: {},
    });

```

<br/><details><summary>Explanation</summary>Something that we often see in people's code is assigning to a result variable
and then immediately returning it.

Returning the result directly shortens the code and removes an unnecessary
variable, reducing the mental load of reading the function.

Where intermediate variables can be useful is if they then get used as a
parameter or a condition, and the name can act like a comment on what the
variable represents. In the case where you're returning it from a function, the
function name is there to tell you what the result is, so the variable name
is unnecessary.
</details>
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +66 to +74
const ret = await sendTRPCMessage({
pluginName: 'posclient',
method: 'query',
method: lastAction === 'crudData' ? 'mutation' : 'query',
module: 'posclient',
action: lastAction,
input: { data: { ...data, token: pos.token } },
defaultValue: '',
input: { ...input },
defaultValue: {},
});
return ret;
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
const ret = await sendTRPCMessage({
pluginName: 'posclient',
method: 'query',
method: lastAction === 'crudData' ? 'mutation' : 'query',
module: 'posclient',
action: lastAction,
input: { data: { ...data, token: pos.token } },
defaultValue: '',
input: { ...input },
defaultValue: {},
});
return ret;
return await sendTRPCMessage({
pluginName: 'posclient',
method: lastAction === 'crudData' ? 'mutation' : 'query',
module: 'posclient',
action: lastAction,
input: { ...input },
defaultValue: {},
});


ExplanationSomething that we often see in people's code is assigning to a result variable
and then immediately returning it.

Returning the result directly shortens the code and removes an unnecessary
variable, reducing the mental load of reading the function.

Where intermediate variables can be useful is if they then get used as a
parameter or a condition, and the name can act like a comment on what the
variable represents. In the case where you're returning it from a function, the
function name is there to tell you what the result is, so the variable name
is unnecessary.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
3 Security Hotspots

See analysis details on SonarQube Cloud

Copy link
Contributor

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Caution

Changes requested ❌

Reviewed everything up to c859769 in 2 minutes and 28 seconds. Click for details.
  • Reviewed 1081 lines of code in 10 files
  • Skipped 0 files when reviewing.
  • Skipped posting 13 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. backend/plugins/posclient_api/src/modules/posclient/trpc/posclient.ts:22
  • Draft comment:
    Refactored input handling: now using const data = input instead of destructuring { data } = input. Verify that all callers provide the expected input shape.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% The comment is asking the PR author to verify that all callers provide the expected input shape, which is against the rules as it asks for verification. It does not provide a specific suggestion or point out a specific issue with the code.
2. backend/plugins/posclient_api/src/modules/posclient/trpc/posclient.ts:68
  • Draft comment:
    Changed 'crudData' procedure from query to mutation. Ensure that any client callers are updated to use mutation for this action.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% The comment is asking the author to ensure that client callers are updated, which is against the rules. It is not making a specific suggestion or pointing out a specific issue with the code.
3. backend/plugins/sales_api/src/initWorker.ts:64
  • Draft comment:
    The method selection in sendTRPCMessage now uses a ternary based on 'lastAction'. Verify that using 'mutation' for 'crudData' doesn’t impact other actions.
  • Reason this comment was not posted:
    Confidence changes required: 50% <= threshold 50% None
4. backend/plugins/sales_api/src/modules/pos/graphql/resolvers/mutations/pos.ts:80
  • Draft comment:
    In the groupsToRemove filter callback, return a boolean instead of 'el._id'. Use 'return index === -1' for clarity.
  • Reason this comment was not posted:
    Comment was on unchanged code.
5. frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts:18
  • Draft comment:
    Schema update: 'productDetailSchema' now uses arrays for excluded IDs and optional 'categoryIds'. Ensure UI and backend are updated to match this new shape.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% The comment is asking the author to ensure that the UI and backend are updated to match the new schema shape. This falls under the rule of not asking the author to ensure behavior is intended or tested. Therefore, this comment should be removed.
6. frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts:25
  • Draft comment:
    In 'catProdMappingSchema', productIds changed to a single 'productId' with optional name/code. Confirm that downstream mapping logic is consistent with this schema.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% The comment is asking the author to confirm that the downstream mapping logic is consistent with the schema change. This falls under asking the author to ensure behavior is intended, which is not allowed.
7. frontend/plugins/sales_ui/src/modules/pos/pos-detail/components/posDetail.tsx:214
  • Draft comment:
    Toast for POS edit uses variant 'destructive'. For a successful edit, consider using a 'success' variant.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While this appears to be a valid observation about incorrect toast styling, our rules explicitly state "Do NOT comment on any pure frontend UI or styling changes, only logic." Toast styling is purely a UI concern and doesn't affect functionality. The toast will still show the success message regardless of its visual style. The toast variant could potentially be considered part of the UX logic rather than pure styling, as it communicates the type of operation to the user. Maybe this is more than just cosmetic. Even if the toast variant has UX implications, it's still fundamentally a UI styling choice. The rules are clear about not commenting on UI/styling changes. Following our rules strictly, we should delete this comment as it relates to UI styling rather than logic.
8. frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx:97
  • Draft comment:
    Typographical notice: The text "categories's" on this line appears to contain an error. If you mean the products belonging to multiple categories, consider changing it to "categories'". Additionally, "synched" is a less common variant; "synced" might be clearer. Finally, if 'pos' refers to a point of sale system, you might consider capitalizing it as "POS" for consistency.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While the comment is technically correct about the grammar and spelling, our rules state that we should only keep comments that require clear code changes. UI text and styling changes are explicitly called out as something we should NOT comment on. The rule states "Do NOT comment on any pure frontend UI or styling changes, only logic. Assume that if the author changed the UI, they did it correctly." The text issues could impact readability and professionalism of the UI. Poor grammar/spelling might confuse users. While readability is important, our rules are clear that UI text changes are out of scope. We must trust the author's judgment on UI-related decisions. Delete this comment as it only addresses UI text formatting which is explicitly out of scope for our review.
9. frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx:122
  • Draft comment:
    Typographical note: The label text "CATEGORY included" may be unintentional. It’s inconsistent with the prior styling (e.g., "PRODUCT *") and might be meant to simply read "CATEGORY" (or be fully capitalized, e.g., "CATEGORY INCLUDED"). Please verify if this is the intended label.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% The comment suggests changing the casing for consistency, but looking at the code, the current casing appears to be an intentional design choice. The UI has a mix of casing styles that seem purposeful. This appears to be a purely stylistic suggestion that doesn't impact functionality or clarity. I could be wrong about the intentionality of the casing styles. There might be a style guide I'm not aware of. Even if there is a style guide, this kind of minor UI text casing suggestion is not important enough to warrant a PR comment. The current text is clear and readable. Delete this comment as it's a minor stylistic suggestion about UI text that doesn't impact functionality or clarity.
10. frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx:162
  • Draft comment:
    Typo: In the label 'Exclude PRODUCT`s', the backtick appears to be a mistake. Consider changing it to 'Exclude PRODUCTS' (or similar) for consistency.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While the comment is technically correct about the backtick being incorrect, this is an extremely minor UI text issue. The rules state not to comment on pure frontend UI changes and to assume the author made UI changes correctly. This falls into that category - it's just label text. The backtick could potentially cause confusion for users or make the UI look unprofessional. Maybe this isn't purely a UI issue since it affects usability? No, this is still fundamentally a UI text issue. The backtick vs apostrophe doesn't affect functionality, and the rules clearly state to trust the author on UI changes. Delete this comment as it violates the rule about not commenting on pure frontend UI changes.
11. frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx:245
  • Draft comment:
    Typo found: The phrase "These categories's products" uses an unnecessary apostrophe. Consider changing it to "These categories' products".
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While the grammar correction is technically correct, our rules state that we should not make purely informative comments that don't require code changes. This is a very minor text change in a UI string, and doesn't affect the functionality of the code. Additionally, we're told not to comment on pure frontend UI changes. The text is customer-facing UI text, so one could argue that proper grammar is important for professionalism. The fix is also very simple and clear. Even for customer-facing text, this is an extremely minor issue that doesn't impact understanding. Our rules explicitly state not to comment on UI changes and to only comment when there's clearly a code change required. Delete this comment as it's a minor UI text change that doesn't impact functionality, and our rules explicitly state not to comment on UI changes.
12. frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx:446
  • Draft comment:
    There appears to be a stray comment marker (*/) at the end of the line. It looks like leftover code from commented-out logic. Please review and remove if it's not needed.
  • Reason this comment was not posted:
    Comment looked like it was already resolved.
13. frontend/plugins/sales_ui/src/modules/pos/create-pos/types/index.ts:59
  • Draft comment:
    Typo: The property name "merchantin" appears to be a typographical error. Consider renaming it to "merchantId" if that is the intended meaning.
  • Reason this comment was not posted:
    Comment was on unchanged code.

Workflow ID: wflow_Y4WIALhNflHpEeu4

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

useEffect(() => {
if (!posDetail) return;

console.log('productGroups', productGroups);
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove or disable debug logging (console.log) in production code.

Suggested change
console.log('productGroups', productGroups);

render={({ field }) => (
<Form.Item>
<Form.Label className="text-sm text-[#A1A1AA] uppercase font-semibold">
Exclude PRODUCT`s
Copy link
Contributor

Choose a reason for hiding this comment

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

Label text "Exclude PRODUCTs"contains an unexpected backtick; consider using a consistent label like"Exclude PRODUCTS"`.

Suggested change
Exclude PRODUCT`s
Exclude PRODUCTS

const variables = operationVariables?.variables || {};
const fieldsToUpdate: Record<string, () => any> = {};

fields.forEach((field) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider adding error handling in the update callback of the mutation, and validate the fields parameter before use.

posId: posDetail._id,
groups: [
...productValues.productGroups.map((item) => ({
_id: `temporaryId.${Math.random()}`,
Copy link
Contributor

Choose a reason for hiding this comment

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

Temporary IDs are generated using Math.random(), which may lead to collisions. Consider using a more robust unique ID generator.

Suggested change
_id: `temporaryId.${Math.random()}`,
_id: `temporaryId.${crypto.randomUUID()}`,

@batorshih batorshih merged commit 78f3496 into erxes:main Oct 21, 2025
7 of 9 checks passed
Copy link

@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: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
backend/plugins/posclient_api/src/modules/posclient/trpc/posclient.ts (1)

71-99: Good: mutate for CRUD; add validation and consistent return

  • Mutation is correct. However, input is z.any() and token is used without guarantees in 'productGroups'/'slots'.
  • Return value is undefined; other endpoints return { status: 'success' }.

Apply minimal guards and a consistent response:

+import { TRPCError } from '@trpc/server';
 ...
- crudData: t.procedure.input(z.any()).mutation(async ({ ctx, input }) => {
+ crudData: t.procedure.input(z.object({
+   type: z.enum(['product','productCategory','user','productGroups','slots']),
+   token: z.string().optional(),
+ }).passthrough()).mutation(async ({ ctx, input }) => {
   const { models, subdomain } = ctx;
   const { token, type } = input;
@@
-      if (type) {
+      if (type) {
         switch (type) {
@@
           case 'productGroups':
-            const { productGroups = [] } = input;
+            const { productGroups = [] } = input as any;
+            if (!token) throw new TRPCError({ code: 'BAD_REQUEST', message: 'token is required for productGroups' });
             await preImportProducts(models, token, productGroups);
             await importProducts(subdomain, models, token, productGroups);
             break;
           case 'slots':
-            const { slots = [] } = input;
+            const { slots = [] } = input as any;
+            if (!token) throw new TRPCError({ code: 'BAD_REQUEST', message: 'token is required for slots' });
             await importSlots(models, slots, token);
             break;
@@
-      }
-    }),
+      }
+      return { status: 'success' };
+    }),
  • Consider converting other write operations (e.g., updateSynced, erxes_posclient_to_pos_api(_remove), paymentCallbackClient) to mutations in a follow-up for semantic consistency.
backend/plugins/sales_api/src/modules/pos/graphql/resolvers/mutations/pos.ts (1)

80-88: Bug: deleteMany uses documents instead of IDs

groupsToRemove holds docs (from dbGroups.filter(...)), but $in expects IDs. Also the filter callback should return boolean, not an _id value.

-    const groupsToRemove = dbGroups.filter((el) => {
-      const index = groupsToUpdate.findIndex((g) => g._id === el._id);
-      if (index === -1) {
-        return el._id;
-      }
-    });
-    if (groupsToRemove.length > 0) {
-      await models.ProductGroups.deleteMany({ _id: { $in: groupsToRemove } });
-    }
+    const groupsToRemoveIds = dbGroups
+      .filter((el) => groupsToUpdate.findIndex((g) => g._id === el._id) === -1)
+      .map((el) => el._id);
+    if (groupsToRemoveIds.length > 0) {
+      await models.ProductGroups.deleteMany({ _id: { $in: groupsToRemoveIds } });
+    }
frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx (1)

118-151: Based on the verification results, I have confirmed that the original review comment is accurate. The schema clearly defines both fields as z.array(z.string()), yet the UI implementation only allows selecting a single value at a time by wrapping it in an array. This is a legitimate type-to-UI mismatch that requires clarification and resolution.

Type-to-UI mismatch: categoryIds/excludedCategoryIds

Schema defines both fields as z.array(z.string()), but the UI component only allows single-select mode (taking only the first element with field.value?.[0] and wrapping selections as [value]). Clarify the intended behavior:

  • If multiple categories should be allowed, update SelectCategory to multi-select
  • If only a single category is needed, narrow the schema types from z.array(z.string()) to z.string()
frontend/plugins/sales_ui/src/modules/pos/pos-detail/components/posDetail.tsx (1)

184-201: Add product fields to cache update list to prevent stale UI

The mutation sends product fields (catProdMappings, initialCategoryIds, kioskExcludeCategoryIds, kioskExcludeProductIds, checkExcludeCategoryIds) to the backend but excludes them from the Apollo cache update. Include them in the fields array to sync the cache after mutation.

      [
        'name',
        'description',
        'allowTypes',
        'scopeBrandIds',
        'branchId',
        'adminTeamMember',
        'adminPrintTempBill',
        'adminDirectSales',
        'adminDirectDiscountLimit',
        'cashierTeamMember',
        'cashierPrintTempBill',
        'cashierDirectSales',
        'cashierDirectDiscountLimit',
        'adminIds',
        'cashierIds',
        'permissionConfig',
+       'catProdMappings',
+       'initialCategoryIds',
+       'kioskExcludeCategoryIds',
+       'kioskExcludeProductIds',
+       'checkExcludeCategoryIds',
      ],
🧹 Nitpick comments (8)
backend/plugins/sales_api/src/modules/pos/graphql/resolvers/mutations/pos.ts (1)

122-129: Slots bulk update looks good; upsert optional

upsert: true for updates is not required since inserts are handled below; leaving it could mask accidental missing IDs.

-          upsert: true,
+          upsert: false,

Also applies to: 132-137

frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts (1)

26-31: Minor schema tweaks look fine

  • Optional _id for mappings/payment types.
  • Optional branchId/departmentId.
  • assignedUserIds defaulted to [].
  • combineFormData includes deliveryConfig.

Consider .strict() on key schemas to catch typos during form submission.

Also applies to: 34-34, 87-87, 112-114, 162-163, 194-198

frontend/plugins/sales_ui/src/modules/pos/create-pos/hooks/usePosCreateForm.tsx (1)

148-153: Type-safe updateFormStepData

Avoid any; make it generic so data matches the selected step’s schema.

-  const updateFormStepData = (step: keyof FormStepData, data: any) => {
-    setFormStepData((prev) => ({
-      ...prev,
-      [step]: data,
-    }));
-  };
+  function updateFormStepData<K extends keyof FormStepData>(
+    step: K,
+    data: NonNullable<FormStepData[K]>
+  ) {
+    setFormStepData(prev => ({ ...prev, [step]: data }));
+  }
frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx (5)

31-40: Default productGroups in form

Initialize productGroups to [] to avoid uncontrolled-to-controlled edge cases when watching the field.

   const internalForm = useForm<ProductFormValues>({
     defaultValues: {
       productDetails: [],
       catProdMappings: [],
       initialCategoryIds: [],
       kioskExcludeCategoryIds: [],
       kioskExcludeProductIds: [],
       checkExcludeCategoryIds: [],
+      productGroups: [],
     },
   });

47-56: Remove debug log

Drop console.log in production code.

-    console.log('productGroups', productGroups);

96-99: Copy tweaks

Minor grammar for clarity.

-            <p className="text-sm text-gray-500">
-              These selected categories's products will be synched in this pos.
-            </p>
+            <p className="text-sm text-gray-500">
+              Products from the selected categories will sync to this POS.
+            </p>

161-173: Typo in label

Use “Products” without a backtick.

-                            Exclude PRODUCT`s
+                            Exclude products

112-156: Array fields: prefer useFieldArray

Managing arrays via watch/setValue works but loses some RHF benefits (dirty tracking, stable keys). useFieldArray is the idiomatic approach for productGroups and catProdMappings.

I can provide a focused diff using useFieldArray for these two sections.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8aaa97b and c859769.

📒 Files selected for processing (10)
  • backend/plugins/posclient_api/src/modules/posclient/trpc/posclient.ts (2 hunks)
  • backend/plugins/sales_api/src/initWorker.ts (1 hunks)
  • backend/plugins/sales_api/src/modules/pos/graphql/resolvers/mutations/pos.ts (7 hunks)
  • frontend/plugins/sales_ui/src/modules/pos/constants/index.tsx (1 hunks)
  • frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts (6 hunks)
  • frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx (14 hunks)
  • frontend/plugins/sales_ui/src/modules/pos/create-pos/hooks/usePosCreateForm.tsx (4 hunks)
  • frontend/plugins/sales_ui/src/modules/pos/create-pos/types/index.ts (1 hunks)
  • frontend/plugins/sales_ui/src/modules/pos/hooks/usePosEditProductGroup.tsx (1 hunks)
  • frontend/plugins/sales_ui/src/modules/pos/pos-detail/components/posDetail.tsx (6 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Write concise, technical TypeScript code
Use functional and declarative programming patterns; avoid classes
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Avoid console logs
Always use absolute paths for imports
Use TypeScript for all code (no .js/.jsx)
Prefer interfaces over types for object shapes
Avoid enums; use maps instead
Use the function keyword for pure functions
Avoid unnecessary curly braces in simple conditionals; use concise syntax

Files:

  • backend/plugins/posclient_api/src/modules/posclient/trpc/posclient.ts
  • frontend/plugins/sales_ui/src/modules/pos/hooks/usePosEditProductGroup.tsx
  • backend/plugins/sales_api/src/initWorker.ts
  • frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx
  • frontend/plugins/sales_ui/src/modules/pos/constants/index.tsx
  • frontend/plugins/sales_ui/src/modules/pos/create-pos/hooks/usePosCreateForm.tsx
  • frontend/plugins/sales_ui/src/modules/pos/pos-detail/components/posDetail.tsx
  • backend/plugins/sales_api/src/modules/pos/graphql/resolvers/mutations/pos.ts
  • frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts
  • frontend/plugins/sales_ui/src/modules/pos/create-pos/types/index.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use declarative JSX
Use Shadcn UI, Radix UI, and Tailwind for components and styling
Implement responsive, mobile-first design with Tailwind CSS
Minimize useEffect and setState usage
Wrap client components in React.Suspense with a fallback
Use dynamic loading for non-critical components
Include explicit width and height (or sizes) when rendering images
Implement lazy loading for images
Limit 'use client': favor server components and Next.js SSR
Use 'use client' only for Web API access in small components
Do not use 'use client' components for data fetching or state management

Files:

  • frontend/plugins/sales_ui/src/modules/pos/hooks/usePosEditProductGroup.tsx
  • frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx
  • frontend/plugins/sales_ui/src/modules/pos/constants/index.tsx
  • frontend/plugins/sales_ui/src/modules/pos/create-pos/hooks/usePosCreateForm.tsx
  • frontend/plugins/sales_ui/src/modules/pos/pos-detail/components/posDetail.tsx
🧬 Code graph analysis (4)
frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx (2)
frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts (1)
  • ProductFormValues (167-167)
frontend/plugins/sales_ui/src/modules/pos/create-pos/types/index.ts (1)
  • ProductGroup (26-32)
frontend/plugins/sales_ui/src/modules/pos/pos-detail/components/posDetail.tsx (4)
frontend/plugins/sales_ui/src/modules/pos/hooks/usePosEditProductGroup.tsx (1)
  • usePosEditProductGroup (4-36)
frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts (1)
  • ProductFormValues (167-167)
frontend/plugins/sales_ui/src/modules/pos/constants/index.tsx (1)
  • TOAST_MESSAGES (148-155)
frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx (1)
  • ProductForm (20-586)
backend/plugins/sales_api/src/modules/pos/graphql/resolvers/mutations/pos.ts (1)
backend/plugins/posclient_api/src/modules/posclient/graphql/resolvers/customResolvers/posConfig.ts (1)
  • slots (5-7)
frontend/plugins/sales_ui/src/modules/pos/create-pos/types/index.ts (1)
frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts (8)
  • BasicInfoFormValues (165-165)
  • PermissionFormValues (166-166)
  • ProductFormValues (167-167)
  • PaymentFormValues (168-168)
  • UiConfigFormValues (169-169)
  • DeliveryConfigFormValues (170-170)
  • FinanceConfigFormValues (171-171)
  • FormStepData (174-183)
⏰ 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: posclient_api-ci
  • GitHub Check: sales_api-ci
  • GitHub Check: Sourcery review
🔇 Additional comments (4)
frontend/plugins/sales_ui/src/modules/pos/constants/index.tsx (1)

148-155: LGTM: added POS_EDITED toast

Consistent naming and usage with existing messages.

backend/plugins/sales_api/src/initWorker.ts (1)

64-75: Action→method mapping: use Set for maintainability; verify flattened input requirement

The Set-based refactoring is valid for future extensibility. However, the verification request about flattened input cannot be confirmed—configs.manage still passes data: { type: 'pos', ...configData } and is properly spread into input with token. Clarify whether this structure needs to change or if the original code is already correct.

backend/plugins/posclient_api/src/modules/posclient/trpc/posclient.ts (1)

23-29: Input shape is correctly flattened by the message handler; no breaking change exists

Your concern assumes callers send wrapped { data }, but investigation shows the only caller (syncPosToClient in sales_api) uses sendPosclientMessage, which flattens the data before reaching the TRPC endpoint. The input received at line 23 is already { type, pos, adminUsers, cashiers, token }, which matches what receivePosConfig destructures at line 436. The assignment const data = input; is correct.

Likely an incorrect or invalid review comment.

frontend/plugins/sales_ui/src/modules/pos/pos-detail/components/posDetail.tsx (1)

202-213: Don’t assign random names to groups

Names like temporaryId.${Math.random()} will persist junk data. Use existing item.name or omit if API allows; otherwise derive a deterministic label.

-  groups: [
-    ...productValues.productGroups.map((item) => ({
-      _id: `temporaryId.${Math.random()}`,
-      name: `temporaryId.${Math.random()}`,
-      categoryIds: item.categoryIds,
-      excludedCategoryIds: item.excludedCategoryIds,
-      excludedProductIds: item.excludedProductIds,
-    })),
-  ],
+  groups: productValues.productGroups.map((item, i) => ({
+    ...(item._id ? { _id: item._id } : {}),
+    ...(item.name ? { name: item.name } : {}),
+    categoryIds: item.categoryIds,
+    excludedCategoryIds: item.excludedCategoryIds,
+    excludedProductIds: item.excludedProductIds,
+  })),

Comment on lines +21 to 22
params.onServer = true; // default pos config
const pos = await models.Pos.posAdd(user, params);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Forcing onServer=true may override explicit false

Setting params.onServer = true and doc.onServer = true unconditionally changes behavior versus env‑gated logic. Preserve explicit false from clients and default only when undefined.

-    params.onServer = true; // default pos config
+    if (typeof params.onServer === 'undefined') {
+      params.onServer = true; // default pos config
+    }
@@
-    doc.onServer = true; // default pos config
+    if (typeof doc.onServer === 'undefined') {
+      doc.onServer = true; // default pos config
+    }

Confirm no flows rely on onServer: false (e.g., remote-only POS).

Also applies to: 42-43


🏁 Script executed:

#!/bin/bash
# Inspect the target file to understand context
cat backend/plugins/sales_api/src/modules/pos/graphql/resolvers/mutations/pos.ts

Length of output: 4105


🏁 Script executed:

#!/bin/bash
# Search for onServer usage throughout the codebase
rg -nP --type=ts --type=tsx 'onServer' -B2 -A2

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search specifically for onServer being set to false
rg -nP --type=ts --type=tsx 'onServer\s*[=:]\s*false' -B3 -A3

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for POS model and posAdd method to understand the flow
rg -nP 'posAdd' -B3 -A5

Length of output: 14026


🏁 Script executed:

#!/bin/bash
# Retry search for onServer usage with correct rg syntax (no tsx type)
rg -nP 'onServer' -B2 -A2 --type ts

Length of output: 17995


🏁 Script executed:

#!/bin/bash
# Search for any usage of onServer: false or onServer false across entire codebase
rg -nP 'onServer.*false|false.*onServer' -B2 -A2

Length of output: 521


🏁 Script executed:

#!/bin/bash
# Check if there are any type definitions for IPos that document onServer
fd -e ts -e js | xargs grep -l 'interface IPos\|type IPos' | head -5

Length of output: 364


Unconditional onServer=true override breaks remote-only POS flows

The code unconditionally sets onServer = true after the environment check, overriding any explicit false from clients. This breaks routing logic: initWorker.ts checks !pos.onServer to queue actions to posclient instead of handling server-side (lines 47-49). The suggested fix is correct—check for undefined before defaulting.

Lines 21-22 and 42-43:

-    params.onServer = true; // default pos config
+    if (typeof params.onServer === 'undefined') {
+      params.onServer = true; // default pos config
+    }

Apply the same pattern to doc.onServer at line 42-43.

🤖 Prompt for AI Agents
In backend/plugins/sales_api/src/modules/pos/graphql/resolvers/mutations/pos.ts
around lines 21-22 and 42-43, the code unconditionally sets params.onServer =
true (and similarly doc.onServer) which overrides explicit false from clients
and breaks remote-only POS flows; change both spots to only set the default when
the value is undefined (e.g., if (params.onServer === undefined) params.onServer
= true) and apply the same undefined-check pattern for doc.onServer so existing
false values passed by clients are preserved.

Comment on lines 132 to 140
export const productSchema = z.object({
productDetails: z.array(productDetailSchema).default([]),
productDetails: z.array(z.string()).default([]),
catProdMappings: z.array(catProdMappingSchema).default([]),
initialCategoryIds: z.array(z.string()).default([]),
kioskExcludeCategoryIds: z.array(z.string()).default([]),
kioskExcludeProductIds: z.array(z.string()).default([]),
checkExcludeCategoryIds: z.array(z.string()).default([]),
productGroups: z.array(productDetailSchema).default([]),
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

productGroups schema likely incomplete vs UI needs

productGroups uses productDetailSchema (no name/description). If UI/backends expect group metadata, define a dedicated schema.

+const productGroupSchema = z.object({
+  _id: z.string().optional(),
+  name: z.string().min(1, 'Group name is required'),
+  description: z.string().default(''),
+  categoryIds: z.array(z.string()).default([]),
+  excludedCategoryIds: z.array(z.string()).default([]),
+  excludedProductIds: z.array(z.string()).default([]),
+  isRequired: z.boolean().default(false),
+});
 ...
-export const productSchema = z.object({
-  productDetails: z.array(z.string()).default([]),
+export const productSchema = z.object({
+  productDetails: z.array(z.string()).default([]),
@@
-  productGroups: z.array(productDetailSchema).default([]),
+  productGroups: z.array(productGroupSchema).default([]),

Verify consumers of productGroups expect objects with name etc. and update types accordingly.


I'll verify this review comment by examining the codebase to understand the schema definitions and how productGroups is used.


🏁 Script executed:

#!/bin/bash
# Find the formSchema.ts file and examine schema definitions
fd -type f -name "formSchema.ts" | head -5

Length of output: 170


🏁 Script executed:

#!/bin/bash
# Search for productDetailSchema definition
rg -n "productDetailSchema\s*=" --type ts

Length of output: 119


🏁 Script executed:

#!/bin/bash
# Search for productGroups usage in the codebase
rg -n "productGroups" --type ts -C 3

Length of output: 34443


🏁 Script executed:

#!/bin/bash
# Search for productGroupSchema if it already exists
rg -n "productGroupSchema\s*=" --type ts

Length of output: 115


🏁 Script executed:

#!/bin/bash
# Search for productSchema usage to understand consumers
rg -n "productSchema" --type ts -C 2 | head -50

Length of output: 3929


🏁 Script executed:

#!/bin/bash
# Look for any group-related schemas or types
rg -n "group.*Schema|GroupSchema" --type ts -i

Length of output: 5107


🏁 Script executed:

#!/bin/bash
# Check for name and description fields being used with productGroups
rg -n "\.name|\.description" --type ts | grep -i group | head -20

Length of output: 2523


🏁 Script executed:

#!/bin/bash
# Examine the specific file and surrounding context
cat frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts 2>/dev/null || echo "File not found in expected location"

Length of output: 6875


🏁 Script executed:

#!/bin/bash
# Search more broadly for formSchema files
fd -type f -name "*formSchema*" --type f

Length of output: 170


🏁 Script executed:

#!/bin/bash
# Search for product group related code patterns
rg -n "productGroup|product.*group" --type ts -i | head -30

Length of output: 3791


🏁 Script executed:

#!/bin/bash
# Check for any imports or references that might indicate schema structure
rg -n "import.*productDetailSchema|import.*productSchema" --type ts -C 1

Length of output: 690


🏁 Script executed:

#!/bin/bash
# Search for object patterns with name and description to understand group expectations
rg -nP "name\s*:\s*z\.string.*description\s*:\s*z\.string" --type ts -A 2 -B 2

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Look for any UI components using productGroups to understand expectations
rg -n "productGroups\?" --type tsx -C 5

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Find all schema definitions in the formSchema file if it exists
fd -type f -name "formSchema.ts" -x cat {}

Length of output: 170


🏁 Script executed:

#!/bin/bash
# Search for type or interface definitions related to product groups
rg -n "interface.*[Gg]roup|type.*[Gg]roup" --type ts | grep -i product | head -20

Length of output: 1679


🏁 Script executed:

#!/bin/bash
# Check API response or backend schema references
rg -n "Group.*name.*description|group.*metadata" --type ts -i -C 2 | head -30

Length of output: 1085


🏁 Script executed:

#!/bin/bash
# Look at all z.object definitions to understand schema patterns
rg -n "const.*Schema\s*=" --type ts | grep -E "(product|group)" | head -20

Length of output: 2150


🏁 Script executed:

#!/bin/bash
# Search in TypeScript files more broadly for schema files
find . -name "*.ts" -o -name "*.tsx" | xargs grep -l "productSchema\|productDetailSchema" 2>/dev/null | head -10

Length of output: 510


🏁 Script executed:

#!/bin/bash
# Get complete view of schemas in the directory
rg -n "export const.*Schema" frontend/plugins/sales_ui/src/modules/pos/create-pos/components/ --type ts -A 5 2>/dev/null | head -100

Length of output: 6668


🏁 Script executed:

#!/bin/bash
# Search for the actual file more carefully with broader path search
find . -path "*sales_ui*" -name "*formSchema*" -type f 2>/dev/null

Length of output: 149


🏁 Script executed:

#!/bin/bash
# Try alternative approach - search all TypeScript files for productSchema definition
rg -l "export const productSchema" --type ts

Length of output: 221


🏁 Script executed:

#!/bin/bash
# Get content of the specific file from any location
rg -A 30 "export const productSchema" --type ts -B 5

Length of output: 11535


🏁 Script executed:

#!/bin/bash
# List files in the likely directory
ls -la frontend/plugins/sales_ui/src/modules/pos/create-pos/components/ 2>/dev/null || echo "Path not found - searching alternatives"

Length of output: 817


🏁 Script executed:

#!/bin/bash
# Search for any POS-related formSchema files
find . -type d -name "create-pos" 2>/dev/null -exec find {} -name "*Schema*" \;

Length of output: 79


🏁 Script executed:

#!/bin/bash
# Broad search for all schema files that mention products
find . -type f \( -name "*.ts" -o -name "*.tsx" \) -exec grep -l "productSchema\|productDetailSchema" {} \; 2>/dev/null | head -15

Length of output: 510


🏁 Script executed:

#!/bin/bash
# Look for complete schema by searching more lines
rg -A 15 "export const productSchema = z.object" --type ts

Length of output: 1887


🏁 Script executed:

#!/bin/bash
# Find where productGroups is actually used
rg "productGroups" --type ts --type tsx -B 5 -A 5 | head -100

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Check what fields are expected in group items
rg -n "\._id|\['_id'\]|\.name|\['name'\]" --type ts | grep -i group -A 2 -B 2 | head -40

Length of output: 4959


🏁 Script executed:

#!/bin/bash
# Directory structure investigation
find . -type d -name "pos" -o -type d -name "create-pos" 2>/dev/null

Length of output: 190


🏁 Script executed:

#!/bin/bash
# Final comprehensive search for schema file content
rg "productDetailSchema\|productGroupSchema" --type ts -B 10 -A 10 | head -150

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for group object creation or typing patterns
rg "name.*description.*category|category.*name" --type ts -i -C 3 | grep -i group -A 3 -B 3 | head -50

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find ProductGroup interface definition in frontend
rg -n "export interface ProductGroup" --type ts -A 10

Length of output: 1057


🏁 Script executed:

#!/bin/bash
# Find IProductGroup interface definition in backend
rg -n "export interface IProductGroup" --type ts -A 15

Length of output: 6851


🏁 Script executed:

#!/bin/bash
# Check the backend productGroupSchema definition
rg -n "export const productGroupSchema" --type ts -A 20

Length of output: 1959


🏁 Script executed:

#!/bin/bash
# Check GraphQL ProductGroups type definition
rg -n "type ProductGroups|type ProductGroups {" --type ts -A 15

Length of output: 141


🏁 Script executed:

#!/bin/bash
# See what GroupInput expects in the mutation
rg -n "GroupInput" --type ts -A 10 -B 2

Length of output: 6072


🏁 Script executed:

#!/bin/bash
# Check backend mutation resolver for productGroupsBulkInsert
rg -n "productGroupsBulkInsert" --type ts -A 20 | head -80

Length of output: 6024


🏁 Script executed:

#!/bin/bash
# Look for group creation/editing in backend models
rg -n "groupsAdd|groupsEdit" --type ts -A 10 -B 2

Length of output: 4576


🏁 Script executed:

#!/bin/bash
# Check how groups are persisted in backend
rg -n "ProductGroups.create|ProductGroup.create" --type ts -A 5 -B 2

Length of output: 709


🏁 Script executed:

#!/bin/bash
# Verify what posDetail stores for productGroups
rg -n "posDetail.*productGroups\|productGroups.*posDetail" --type ts -C 3 | head -50

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check if there's form validation or TypeScript type for productGroups
rg -n "type ProductFormValues" --type ts -A 20

Length of output: 3022


🏁 Script executed:

#!/bin/bash
# Look at the GraphQL schema query return type for productGroups
rg -n "query productGroups" --type ts -A 15

Length of output: 1326


🏁 Script executed:

#!/bin/bash
# Check if there's any group name/description field being used in the UI
rg -n "productGroups\[.*\]\.name|productGroups.*\.description" --type ts -C 2

Length of output: 0


Schema mismatch confirmed; suggested fix needs revision

The review comment correctly identifies a critical schema mismatch. However, the proposed schema fix has issues:

Verified Problems:

  • Frontend's productDetailSchema (used for productGroups) lacks name and description fields
  • Backend's IProductGroup interface requires name (string) and description (string) as mandatory fields
  • GraphQL GroupInput and the productGroups query return name, description, categoryIds, excludedCategoryIds, and excludedProductIds
  • Frontend form renders only categoryIds, excludedCategoryIds, excludedProductIds, and isRequired—no fields for group name/description
  • Data transformation generates dummy names (temporaryId.${Math.random()}) instead of capturing user input

Issues with the Proposed Diff:

  • Includes isRequired field (belongs to productDetailSchema, not product group metadata)
  • Missing posId (backend requires it)
  • Suggested schema structure mixes concerns

Correct approach:
Define a separate productGroupSchema with fields: _id, name, description, categoryIds, excludedCategoryIds, excludedProductIds (without isRequired). Update the UI form to include name and description inputs, and remove the temporary name workaround.


export const productSchema = z.object({
productDetails: z.array(productDetailSchema).default([]),
productDetails: z.array(z.string()).default([]),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Breaking change: productDetails now string[]

All consumers that previously used objects will break. Confirm UI/components and API mappers are updated.


🏁 Script executed:

#!/bin/bash
# Inspect usages of productDetails
rg -nP -C3 --type=ts --type=tsx '\bproductDetails\b'

Length of output: 27


I'll correct the search strategy and gather information about the productDetails changes.


---

🏁 Script executed:

```shell
#!/bin/bash
# View the formSchema.ts file around line 133
cat frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts

Length of output: 6875


🏁 Script executed:

#!/bin/bash
# Search for productDetails usages - simplified rg syntax
rg -nC3 'productDetails' --type=ts

Length of output: 16289


🏁 Script executed:

#!/bin/bash
# Broader search for productDetails across all file types
rg -nC3 'productDetails'

Length of output: 16289


Update productSchema.productDetails type or add mapping layer in consumers

Line 133 changes productSchema.productDetails to z.array(z.string()), but consumers receive ProductDetail[] objects:

  • product.tsx:49 resets form with posDetail.productDetails (objects from IPos.ts:34)
  • usePosAdd.tsx:163 already maps objects (pd.productId) for API submission
  • Type mismatch: ProductFormValues now declares string[] while receiving objects

Update either:

  1. Keep productSchema as z.array(productDetailSchema) to match actual data, or
  2. Add explicit mapping in product.tsx and pos-detail components to transform ProductDetail[] to string[] before form reset

Verify type consistency across product.tsx, pos-detail.tsx, and usePosAdd.tsx.

🤖 Prompt for AI Agents
In frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts
around line 133, productDetails was changed to z.array(z.string()) but the app
actually passes ProductDetail[] objects to the form; update either the schema
back to z.array(productDetailSchema) to match consumers or update all form
consumers (product.tsx and pos-detail.tsx) to map ProductDetail[] -> string[]
(extract productId) before calling form.reset; ensure usePosAdd.tsx mapping
remains correct and types for ProductFormValues reflect string[] if you choose
mapping, or ProductDetail[] if you keep the richer schema so all three files and
IPos.ts stay consistent.

Comment on lines +26 to +32
export interface ProductGroup {
name: string;
description?: string;
categoryIds?: string[];
excludedCategoryIds?: string[];
excludedProductIds?: string[];
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Align ProductGroup type with UI usage

UI binds productGroups.[i].isRequired, but the interface lacks it. Add an optional isRequired or remove the field from UI to keep types consistent.

 export interface ProductGroup {
   name: string;
   description?: string;
   categoryIds?: string[];
   excludedCategoryIds?: string[];
   excludedProductIds?: string[];
+  isRequired?: boolean;
 }

🏁 Script executed:

#!/bin/bash
# Double-check for any existing declarations of isRequired on ProductGroup
rg -n -C2 -P 'ProductGroup.*isRequired' frontend

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find where ProductGroup is used in the UI
rg -n -C3 'ProductGroup' frontend/plugins/sales_ui/src --type=ts --type=tsx | head -50

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for isRequired in the sales_ui plugin
rg -n 'isRequired' frontend/plugins/sales_ui/src --type=ts --type=tsx

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Find the component/file that uses productGroups and check for binding
rg -n -C4 'productGroups\s*\[' frontend/plugins/sales_ui/src --type=ts --type=tsx

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Find where ProductGroup is used in the UI (without type flags)
rg -n -C3 'ProductGroup' frontend/plugins/sales_ui/src

Length of output: 14880


🏁 Script executed:

#!/bin/bash
# Search for isRequired in the sales_ui plugin (without type flags)
rg -n 'isRequired' frontend/plugins/sales_ui/src

Length of output: 502


🏁 Script executed:

#!/bin/bash
# Find the component/file that uses productGroups and check for binding
rg -n -C4 'productGroups\s*\[' frontend/plugins/sales_ui/src

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Get context around formSchema.ts line 22 to see if it's ProductGroup schema
rg -n -B5 -A5 'isRequired.*z\.boolean' frontend/plugins/sales_ui/src/modules/pos/create-pos/components/formSchema.ts

Length of output: 373


🏁 Script executed:

#!/bin/bash
# Get context around the productGroups.${index}.isRequired binding in product.tsx
rg -n -B3 -A3 'productGroups.*isRequired' frontend/plugins/sales_ui/src/modules/pos/create-pos/components/product/product.tsx

Length of output: 325


Add isRequired field to ProductGroup interface

The UI form binds to productGroups.[i].isRequired (line 182 in product.tsx) and the form schema validates it, but the ProductGroup interface lacks this field. Add the optional field to align types with runtime expectations.

 export interface ProductGroup {
   name: string;
   description?: string;
   categoryIds?: string[];
   excludedCategoryIds?: string[];
   excludedProductIds?: string[];
+  isRequired?: boolean;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface ProductGroup {
name: string;
description?: string;
categoryIds?: string[];
excludedCategoryIds?: string[];
excludedProductIds?: string[];
}
export interface ProductGroup {
name: string;
description?: string;
categoryIds?: string[];
excludedCategoryIds?: string[];
excludedProductIds?: string[];
isRequired?: boolean;
}
🤖 Prompt for AI Agents
In frontend/plugins/sales_ui/src/modules/pos/create-pos/types/index.ts around
lines 26 to 32, the ProductGroup interface is missing the isRequired field
referenced by the UI form; add an optional property isRequired?: boolean to the
interface so the type matches runtime/form schema expectations.

Comment on lines +20 to +33
return saveProductGroups({
...operationVariables,
variables,
update: (cache, { data }) => {
const productGroupsBulkInsert = data?.productGroupsBulkInsert;
if (!productGroupsBulkInsert) return;

cache.modify({
id: cache.identify(productGroupsBulkInsert),
fields: fieldsToUpdate,
});
},
});
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Guard cache.modify target and make update optional

If cache.identify returns undefined (array/unnormalized object), cache.modify will hit ROOT_QUERY. Also skip update when fields is empty (as in current caller).

-    return saveProductGroups({
-      ...operationVariables,
-      variables,
-      update: (cache, { data }) => {
-        const productGroupsBulkInsert = data?.productGroupsBulkInsert;
-        if (!productGroupsBulkInsert) return;
-
-        cache.modify({
-          id: cache.identify(productGroupsBulkInsert),
-          fields: fieldsToUpdate,
-        });
-      },
-    });
+    return saveProductGroups({
+      ...operationVariables,
+      variables,
+      update: fields.length
+        ? (cache, { data }) => {
+            const entity = data?.productGroupsBulkInsert;
+            if (!entity) return;
+            const entityId = cache.identify(entity);
+            if (!entityId) return;
+            cache.modify({ id: entityId, fields: fieldsToUpdate });
+          }
+        : undefined,
+    });

Run to confirm the mutation field name and normalized key:


🏁 Script executed:

#!/bin/bash
# Find mutation and return type for product group save
rg -n -C3 -P '(mutation\s+saveProductGroups|productGroupsBulkInsert)' --type=ts --type=tsx --type=graphql
# Look for GraphQL type with key fields (__typename/id/_id) that identify productGroupsBulkInsert
rg -n -C2 -P '(type|interface)\s+.*ProductGroup|productGroupsBulkInsert|@key' --type=graphql --type=ts --type=tsx

Length of output: 59




🏁 Script executed:

#!/bin/bash
# Search without file type restrictions for the mutation and related types
rg -n -C3 'saveProductGroups|productGroupsBulkInsert' --multiline

Length of output: 5211


🏁 Script executed:

#!/bin/bash
# Check the target hook file directly
cat "frontend/plugins/sales_ui/src/modules/pos/hooks/usePosEditProductGroup.tsx"

Length of output: 1005


🏁 Script executed:

#!/bin/bash
# Search for GraphQL files with different extensions
fd -e 'gql' -e 'graphql' -e 'ts' -e 'tsx' | head -20

Length of output: 1195


🏁 Script executed:

#!/bin/bash
# Look for GraphQL schema or mutation definitions in common locations
find . -type f \( -name "*.graphql" -o -name "*.gql" -o -name "*schema*" -o -name "*mutation*" \) 2>/dev/null | grep -i "product\|group" | head -20

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Search for fieldsToUpdate definition and usage
rg -n 'fieldsToUpdate' --multiline -C5

Length of output: 32283


🏁 Script executed:

#!/bin/bash
# Check Apollo cache usage patterns in the codebase
rg -n 'cache\.modify' --multiline -C3 | head -40

Length of output: 4197


🏁 Script executed:

#!/bin/bash
# Find cache.identify usage patterns
rg -n 'cache\.identify' --multiline -C2 | head -30

Length of output: 3017


Fix cache.modify to handle array return and guard undefined cache.identify

The mutation returns an array [ProductGroups], not a single object. Passing the array to cache.identify() returns undefined, causing cache.modify() to corrupt ROOT_QUERY. Additionally, skip cache update when no fields to modify.

     return saveProductGroups({
       ...operationVariables,
       variables,
-      update: (cache, { data }) => {
-        const productGroupsBulkInsert = data?.productGroupsBulkInsert;
-        if (!productGroupsBulkInsert) return;
-
-        cache.modify({
-          id: cache.identify(productGroupsBulkInsert),
-          fields: fieldsToUpdate,
-        });
-      },
+      update: fields.length
+        ? (cache, { data }) => {
+            const items = data?.productGroupsBulkInsert;
+            if (!Array.isArray(items) || items.length === 0) return;
+            items.forEach((item) => {
+              const itemId = cache.identify(item);
+              if (itemId) {
+                cache.modify({ id: itemId, fields: fieldsToUpdate });
+              }
+            });
+          }
+        : undefined,
     });

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In frontend/plugins/sales_ui/src/modules/pos/hooks/usePosEditProductGroup.tsx
around lines 20 to 33, the mutation returns an array but the code calls
cache.identify on the whole array (yielding undefined) and unconditionally calls
cache.modify; change the update logic to treat productGroupsBulkInsert as an
array, skip the update if fieldsToUpdate is falsy or empty, and for each product
group in the array call cache.identify(item) and only call cache.modify when
identify returns a valid id; also guard against productGroupsBulkInsert being
undefined before iterating.

Comment on lines +164 to +172
catProdMappings: [
...productValues.catProdMappings.map((item) => ({
_id: `${Math.random()}`,
name: item.name,
code: item.code,
categoryId: item.categoryId,
productId: item.productId,
})),
],
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Avoid random _id for mappings

Random IDs break update semantics and can create duplicates. Preserve existing _id if present; otherwise omit it and let the API generate one.

-      catProdMappings: [
-        ...productValues.catProdMappings.map((item) => ({
-          _id: `${Math.random()}`,
-          name: item.name,
-          code: item.code,
-          categoryId: item.categoryId,
-          productId: item.productId,
-        })),
-      ],
+      catProdMappings: productValues.catProdMappings.map(item => ({
+        ...(item._id ? { _id: item._id } : {}),
+        name: item.name,
+        code: item.code,
+        categoryId: item.categoryId,
+        productId: item.productId,
+      })),

🏁 Script executed:

#!/bin/bash
# Verify whether _id is optional in mapping schema
rg -n -C2 -P 'catProdMappingSchema|catProdMappings' frontend
rg -n -C2 -P 'z\.object\(\{[^}]*_id' frontend

Length of output: 14881


Avoid random _id for mappings

Random IDs break update semantics. MongoDB database drivers by default generate an ObjectID identifier that is assigned to the _id field of each document. Preserve existing _id if present; otherwise omit it and let the API generate one. The schema confirms _id is optional (z.string().optional() in formSchema.ts), so passing it is not required.

-      catProdMappings: [
-        ...productValues.catProdMappings.map((item) => ({
-          _id: `${Math.random()}`,
-          name: item.name,
-          code: item.code,
-          categoryId: item.categoryId,
-          productId: item.productId,
-        })),
-      ],
+      catProdMappings: productValues.catProdMappings.map(item => ({
+        ...(item._id ? { _id: item._id } : {}),
+        name: item.name,
+        code: item.code,
+        categoryId: item.categoryId,
+        productId: item.productId,
+      })),
🤖 Prompt for AI Agents
In frontend/plugins/sales_ui/src/modules/pos/pos-detail/components/posDetail.tsx
around lines 164 to 172, the code assigns a random string to _id for each
catProdMappings entry which breaks update semantics; change the mapping so it
preserves item._id when it exists and otherwise does not set _id (omit the
field) so the API/database can generate it; implement this by only including _id
in the mapped object when item._id is present (or by setting it to
undefined/omitting the property) and keep all other fields unchanged.

Comment on lines +214 to +222
toast({
title: TOAST_MESSAGES.POS_EDITED,
description: `Saved`,
variant: 'destructive',
});
if (posEditResponse.data?.posEdit?._id) {
await productGroupSave({ variables: finalDataProductGroup }, []);
refetchProductGroup();
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Toast timing/variant

Don’t show success before mutations complete, and use a success variant.

-    toast({
-      title: TOAST_MESSAGES.POS_EDITED,
-      description: `Saved`,
-      variant: 'destructive',
-    });
-    if (posEditResponse.data?.posEdit?._id) {
-      await productGroupSave({ variables: finalDataProductGroup }, []);
-      refetchProductGroup();
-    }
+    if (posEditResponse.data?.posEdit?._id) {
+      await productGroupSave({ variables: finalDataProductGroup }, []);
+      await refetchProductGroup();
+      toast({ title: TOAST_MESSAGES.POS_EDITED, variant: 'success' });
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
toast({
title: TOAST_MESSAGES.POS_EDITED,
description: `Saved`,
variant: 'destructive',
});
if (posEditResponse.data?.posEdit?._id) {
await productGroupSave({ variables: finalDataProductGroup }, []);
refetchProductGroup();
}
if (posEditResponse.data?.posEdit?._id) {
await productGroupSave({ variables: finalDataProductGroup }, []);
await refetchProductGroup();
toast({ title: TOAST_MESSAGES.POS_EDITED, variant: 'success' });
}
🤖 Prompt for AI Agents
In frontend/plugins/sales_ui/src/modules/pos/pos-detail/components/posDetail.tsx
around lines 214 to 222, the toast is shown before the related mutations finish
and uses the destructive variant; move the toast so it runs after the
productGroupSave and refetchProductGroup complete, await productGroupSave and
await refetchProductGroup (if they return promises), and change the toast
variant to 'success' (and adjust description to reflect completion, e.g. "Saved"
after awaits). Ensure the toast is only shown when the mutation(s) succeed.

Enkhtuvshin0513 pushed a commit that referenced this pull request Oct 21, 2025
* fix(posclient):  merge

* fix(pos):  moved into sales

* fix(posclient): update

* fix(posclient): code reviews improvements

* fix(posclient): code reviews

* fix(posclient): code reviews

* fix(posclient): code reviews

* fix(posclient): code reviews

* fix(posclient): code reviews

* fix(posclient): code reviews

* fix: pos productgroup&edits

---------

Co-authored-by: orshih <[email protected]>
@coderabbitai coderabbitai bot mentioned this pull request Nov 4, 2025
@coderabbitai coderabbitai bot mentioned this pull request Dec 5, 2025
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.

2 participants