Skip to content

feat(medusa): add Medusa v2 connector - #436

Open
kiegsgroot wants to merge 36 commits into
frappe:developfrom
the-groots:feat/medusa-connector
Open

feat(medusa): add Medusa v2 connector#436
kiegsgroot wants to merge 36 commits into
frappe:developfrom
the-groots:feat/medusa-connector

Conversation

@kiegsgroot

@kiegsgroot kiegsgroot commented Jun 25, 2026

Copy link
Copy Markdown

Add a Medusa (v2) connector

This adds Medusa (the open-source composable-commerce platform, v2) as a first-class connector, built to match the existing Shopify / Amazon / Unicommerce / Zenoti connectors — reusing the shared framework (Ecommerce Item, Ecommerce Integration Log, the controllers/ base classes) rather than introducing anything parallel.

What it does

Flow Direction Trigger
Products + variants ↔ Items both webhook/poll + Item doc-events
Orders → Sales Orders Medusa → ERPNext order.placed (+ scheduled backfill)
Customers + addresses Medusa → ERPNext inline on order
Payment captured → Sales Invoice + Payment Entry Medusa → ERPNext order.payment_captured
Fulfillment → Delivery Note Medusa → ERPNext fulfillment.created
Return → Credit Note (full + partial, qty-prorated tax) Medusa → ERPNext order.return_received
Inventory levels ERPNext → Medusa scheduled, throttled delta

It follows the Shopify reference closely: a single Medusa Setting doctype owning enable/auth/defaults + warehouse/tax mappings; one HMAC-verified ingestion endpoint that enqueues handlers keyed to an Ecommerce Integration Log; the dummy price-list / tax-category trick to bypass ERPNext re-pricing; and a from_integration guard to avoid push/pull echo loops. It also closes two gaps the others leave open — returns → credit notes (ported from Unicommerce) and optional real price-list pricing.

The one architectural wrinkle

Medusa v2 has no admin API to register webhooks (unlike Shopify's Webhook.create). Events come from server-side subscribers. So the connector ships a small companion subscriber (medusa/medusa_subscriber/, TypeScript) that the merchant installs in their Medusa app; it HMAC-signs and forwards events to the ingestion endpoint. A scheduled poll provides backfill/reconciliation. Open question for reviewers: whether you'd prefer that subscriber to live here, as a docs snippet, or in a separate companion repo — easy to move.

Testing

Integration tests for product, order, inventory and return flows (network-free fake client + JSON fixtures). Verified green against a real ERPNext 16.25 install + a live Medusa v2.4.0 instance — every field mapping was confirmed against the @medusajs/types 2.4.0 definitions. 12 tests pass.

Notes for review

  • No new Python dependencies (uses requests, already shipped with Frappe).
  • Custom fields are installed on enable and removed on uninstall; a migration patch covers existing sites.
  • One incidental commit (fix(tests): make before_tests tax-account creation idempotent) lets the shared test bootstrap run on current ERPNext, which auto-creates the GST account — happy to split this into its own PR.
  • Known limitation: the Sales Invoice flow assumes the ERPNext company currency matches the Medusa region currency (the common single-currency setup); multi-currency orders need a customer receivable account in that currency. Documented in the connector README.

Commits

Logically split: framework + Setting → product/customer/order → invoice/fulfillment/returns → inventory → hooks/patches/uninstall → subscriber → tests/docs.

kiegsgroot and others added 9 commits June 25, 2026 15:37
Add the Medusa v2 connector foundation: shared constants, the Admin API client and HMAC-verified webhook ingestion endpoint, the log helper, and the single Medusa Setting doctype (with warehouse, tax-account and webhook child tables) subclassing the shared SettingController. Registers the 'medusa' module.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Import Medusa products (including variants) as ERPNext Items - lazily during order sync or via the Import Products desk page - and push new/updated ERPNext items back to Medusa. Create Customers, Addresses and submitted Sales Orders (with tax, shipping and per-unit discount mapping) from Medusa orders; support historical backfill and order cancellation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On captured payment create a Sales Invoice and Payment Entry; on fulfillment create a Delivery Note matched to the fulfilled lines; on return create a credit note with full and partial (qty-prorated) tax handling, robust across ERPNext versions that drop item_wise_tax_detail.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Scheduled, self-throttled delta inventory sync from ERPNext Bins to Medusa inventory location levels, reusing the shared inventory controller and per-item sync watermark.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire the Item doc-events for ERPNext->Medusa product push, the inventory and backfill scheduler events, a migration patch that installs the custom fields on existing sites, and removal of the connector's custom fields on uninstall.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Medusa v2 exposes no admin API to register webhooks, so ship a small TypeScript subscriber that HMAC-signs and forwards order, fulfillment and return events to the ERPNext ingestion endpoint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Integration tests for the product, order, inventory and return flows backed by a network-free fake client and JSON fixtures, plus connector documentation. Verified green against ERPNext 16.25 and Medusa v2.4.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Newer ERPNext auto-creates GST accounts during company setup; skip creating a duplicate Output Tax GST account so the shared test bootstrap runs on ERPNext 16.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add Medusa (v2) to the README's supported-integrations list, linking to the connector documentation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kiegsgroot
kiegsgroot marked this pull request as ready for review June 25, 2026 19:59
@kiegsgroot
kiegsgroot requested a review from ankush as a code owner June 25, 2026 19:59
@greptile-apps

greptile-apps Bot commented Jun 25, 2026

Copy link
Copy Markdown

Confidence Score: 4/5

This is close, but the shipping total calculation should be fixed before merging.

  • Shipping can be added once per taxed line.
  • Shipping can be omitted when the order has no taxable item lines.
  • The affected path writes incorrect Sales Order and invoice totals.

Files Needing Attention: ecommerce_integrations/medusa/order.py

Reviews (13): Last reviewed commit: "Update ecommerce_integrations/medusa/ret..." | Re-trigger Greptile

Comment thread ecommerce_integrations/medusa/order.py Outdated
Comment thread ecommerce_integrations/medusa/fulfillment.py Outdated
Comment thread ecommerce_integrations/medusa/product.py
Comment thread ecommerce_integrations/medusa/product.py Outdated
Comment thread ecommerce_integrations/medusa/inventory.py Outdated
Comment thread ecommerce_integrations/medusa/order.py
Comment thread ecommerce_integrations/medusa/returns.py
kiegsgroot and others added 4 commits June 25, 2026 17:08
Net each line's per-unit discount into the Sales Order/Invoice rate so ERPNext totals match the Medusa order total instead of overstating the customer balance. Backfill now replays paid/fulfilled/canceled state for historical orders (which won't get live webhooks) and commits the Sales Order before each replay so an independent replay failure can't roll it back.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Match Delivery Note rows strictly on the Medusa order line id; drop the fallbacks that shipped a single line's qty across all rows, or shipped the whole order, when line metadata was missing. An unmatched fulfillment now raises instead of creating incorrect stock movements.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Require System Manager for the product-import endpoints. Link an existing ERPNext Item when a variant SKU already matches (instead of inserting a duplicate). Surface (not swallow) Ecommerce Item link failures after a Medusa product is created. Re-sync products that gained a variant. Stop advancing the inventory watermark when a SKU lookup returns nothing, so a transient empty result retries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add fulfillment tests (full, partial, idempotent, and the fail-safe unmatched case). Assert the Sales Order grand total reflects the discount, and add a backfill test that the paid+fulfilled historical order replays SO + Sales Invoice + Delivery Note. Allow negative stock in the test bootstrap so Delivery Notes submit without seeding stock. Verified green against ERPNext 16.25 + Medusa v2.4.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kiegsgroot

Copy link
Copy Markdown
Author

Thanks for the thorough review — addressed all eight valid findings (one looks like a false positive, noted below). All changes were re-validated against a real ERPNext 16.25 install + Medusa v2.4.0, and the connector test suite (now incl. fulfillment) is green: product 3, order 5, inventory 2, returns 3, fulfillment 4.

# Finding Resolution
2 (P1) Discounts didn't reduce totals Net the per-unit discount into the SO/SI rate so totals match the Medusa order total (869d852). Added an assertion on grand_total.
1 (P1, sec) Import endpoints lacked authz frappe.only_for("System Manager") on every whitelisted import method (ef2fae0).
5 (P1) Created products lost their link Link-insert failures are surfaced as an Error log instead of being swallowed as success (ef2fae0).
3 (P1) Partial fulfillment shipped wrong rows Match strictly on the Medusa order line id; removed the ship-one-qty-everywhere / ship-whole-order fallbacks; an unmatched fulfillment now raises rather than moving wrong stock (107eb22). Added full/partial/unmatched tests.
4 (P2) Variant SKU collided with Items A variant whose SKU already exists as an Item is now linked instead of duplicate-inserted (ef2fae0).
8 (P2) Backfill dropped paid/fulfilled state Backfill replays SI/Payment/DN (and cancellation) for historical orders, committing the SO first so a replay failure can't roll it back (869d852). Added a backfill test.
7 (P2) Missing variant advanced the watermark A not-found SKU lookup no longer advances inventory_synced_on, so a transient empty result retries (ef2fae0).
6 (P2) Re-import hid new variants Bulk import now reconciles every variant, not just the template, so a product that gained a variant is re-synced (ef2fae0).
9 (P2) Tax proration divides by zero I believe this is already guarded: the per-item path does full_qty = …; if not full_qty: … continue before the division (returns.py), so a zero/None full qty is skipped. Happy to add an explicit comment if you'd still like it clearer.

🤖 Generated with Claude Code

Comment thread ecommerce_integrations/medusa/fulfillment.py Outdated
Comment thread ecommerce_integrations/medusa/connection.py
Comment thread ecommerce_integrations/medusa/inventory.py
kiegsgroot and others added 4 commits June 25, 2026 17:50
Subscribe to the order-scoped events that actually exist and carry order context: order.completed (was the non-existent order.payment_captured) and order.fulfillment_created (was fulfillment.created, which carries only a fulfillment id). The delivery-note handler now resolves the full order from an {order_id, fulfillment_id} payload, and the companion subscriber extracts the order id per event (data.id vs data.order_id) and forwards the order (or {order_id, return}) the ERPNext handlers expect. Verified the event names + payloads against Medusa v2.4.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
process_request now returns early (logging Invalid) when the Medusa Setting is disabled, so a stale subscriber holding the secret can't drive document-mutating handlers as Administrator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Separate the SKU->inventory-item lookup from the location-level update. A lookup failure (incl. a transient 404) no longer advances inventory_synced_on, so the row retries; only a 404 from the location-level update (item resolved but gone on Medusa) marks it synced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a test that an order.fulfillment_created {order_id, fulfillment_id} payload resolves the order and still creates the Delivery Note, and a test that a signed event is ignored (logged Invalid, no Sales Order) while the integration is disabled. Verified green against ERPNext 16.25 + Medusa v2.4.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kiegsgroot

Copy link
Copy Markdown
Author

Thanks — addressed all three, and the fulfillment one turned out to be the tip of a bigger event-layer issue worth fixing together. Re-validated against ERPNext 16.25 + Medusa v2.4.0 (suite now 19 green incl. the two new cases below).

Finding Resolution
Fulfillment payload mismatch (fulfillment.py) Switched to the order-scoped order.fulfillment_created event (fulfillment.created carries only a fulfillment id). The handler now resolves the full order from an {order_id, fulfillment_id} payload, and the subscriber forwards the order (b860f32). While verifying against Medusa v2.4.0 I also found order.payment_captured isn't a real event (only order.completed exists) and that order.fulfillment_created/order.return_received carry order_id/return_id rather than id — so the subscriber now extracts the right id per event and forwards the order (or {order_id, return}).
Disabled webhooks still run (connection.py) process_request now returns early (logging Invalid) when the integration is disabled, so a stale subscriber can't drive handlers as Administrator — applied your suggested guard (bd8beaa). Added a test.
Lookup 404 advances watermark (inventory.py) Split the SKU lookup from the location-level update: a lookup failure (incl. a transient 404) no longer advances inventory_synced_on (it retries); only a 404 from the location-level update marks the row synced (3122ad5).

🤖 Generated with Claude Code

Comment thread ecommerce_integrations/medusa/doctype/medusa_setting/medusa_setting.py Outdated
Comment thread ecommerce_integrations/medusa/constants.py
Comment thread ecommerce_integrations/medusa/connection.py
kiegsgroot and others added 3 commits June 25, 2026 18:16
… webhooks

Only create a Sales Invoice + Payment Entry when the Medusa order has a captured payment, so an order.completed event for an uncaptured order is no longer recorded as paid. Downstream handlers (invoice, delivery note, credit note, cancellation) now ensure their prerequisite Sales Order / Invoice exists - creating it from the order if a webhook arrived before order.placed finished - instead of dropping the state as Invalid with no retry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fetch_medusa_locations appended stock-location rows with no ERPNext warehouse and then saved, which validate() rejects - so the very first fetch failed. Mirror the Shopify connector: append the rows and let the client refresh the table; the operator maps warehouses and saves the form.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…etch

Add tests that an uncaptured order is not invoiced and that an out-of-order order.completed still creates both the Sales Order and Sales Invoice, plus that fetch_medusa_locations appends rows without saving. Verified green against ERPNext 16.25 + Medusa v2.4.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kiegsgroot

Copy link
Copy Markdown
Author

Addressed all three — re-validated against ERPNext 16.25 + Medusa v2.4.0 (suite now 22 green across 7 modules).

Finding Resolution
Invoice before capture (constants.py/invoice) prepare_sales_invoice now checks the order's payment_collections for a captured payment before creating the Sales Invoice + Payment Entry, so an order.completed for an uncaptured order is no longer recorded as paid. The backfill path shares the same gate (2fceb41). Added a test.
Webhook ordering drops state (connection.py) Downstream handlers no longer give up with Invalid when the Sales Order/Invoice is missing — they ensure it from the order first (idempotently), so order.completed / order.fulfillment_created / order.return_received / order.canceled arriving before order.placed finishes still produce the invoice, delivery note, credit note, or cancellation (2fceb41). Added an out-of-order test.
Location fetch cannot save (medusa_setting.py) fetch_medusa_locations no longer calls self.save() (which validate() rejects for rows without a warehouse). It appends the rows and lets the client refresh the table — the operator maps warehouses and saves the form, matching the Shopify connector (c4a22c6). Added a test.

🤖 Generated with Claude Code

Comment thread ecommerce_integrations/medusa/order.py
kiegsgroot and others added 2 commits June 25, 2026 18:36
…etch

ensure_sales_order now submits a pre-existing draft Sales Order (from a failed prior run or manual import) instead of skipping on sync_sales_order's dedup, so invoice/fulfillment/return/cancel replay isn't blocked. fetch_medusa_locations now checks write permission on the Medusa Setting before making credentialed Medusa calls and replacing the warehouse mapping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Assert a leftover draft Sales Order is submitted by ensure_sales_order rather than blocking replay. Verified green against ERPNext 16.25 + Medusa v2.4.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kiegsgroot

Copy link
Copy Markdown
Author

Both addressed — re-validated against ERPNext 16.25 + Medusa v2.4.0 (suite 23 green).

Finding Resolution
Draft orders block replay (order.py) ensure_sales_order now submits a pre-existing draft Sales Order (left by a failed run or manual import) instead of skipping on sync_sales_order's id dedup. So invoice / fulfillment / return / cancel replay is no longer blocked when a draft with the Medusa id exists (55791e7). Added a test.
Check setting permission (medusa_setting.py) fetch_medusa_locations now calls self.check_permission("write") before making credentialed Medusa calls and replacing the warehouse-mapping table, so only users who can write the Medusa Setting can invoke it (55791e7).

🤖 Generated with Claude Code

Comment thread ecommerce_integrations/medusa/returns.py
Comment thread ecommerce_integrations/medusa/order.py
kiegsgroot and others added 2 commits June 25, 2026 18:59
…turns

The historical backfill now replays returns as credit notes (fetching them from Medusa when not embedded on the order), so a returned historical order is not left billed with overstated accounting. And a return whose line items match no invoice row now raises instead of crediting the entire invoice (mirrors the fulfillment fail-safe).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Assert backfill replays a credit note for a returned historical order, and that an unmatched return creates no credit note (no over-credit). Verified green against ERPNext 16.25 + Medusa v2.4.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kiegsgroot

Copy link
Copy Markdown
Author

Both addressed — re-validated against ERPNext 16.25 + Medusa v2.4.0 (suite 24 green).

Finding Resolution
Backfill skips returns (order.py) The historical replay now replays returns as credit notes too — using the order's embedded returns when present, otherwise fetching them from Medusa — so a returned historical order isn't left billed/fulfilled with overstated accounting. Added a backfill credit-note assertion.
Unmatched returns overcredit (returns.py) When a return names items but none match a credit-note row, the handler now raises (fail-safe) instead of treating it as a full return and crediting every invoice line. Added a test that an unmatched return produces no credit note.

🤖 Generated with Claude Code

Comment thread ecommerce_integrations/medusa/order.py Outdated
Comment thread ecommerce_integrations/medusa/product.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Comment thread ecommerce_integrations/medusa/returns.py
Comment thread ecommerce_integrations/medusa/product.py
kiegsgroot and others added 2 commits June 25, 2026 19:42
When a Medusa order's currency differs from the company currency, book the Sales Order/Invoice in the order currency with a conversion_rate from ERPNext's exchange-rate lookup, point the invoice debit_to at an auto-created per-currency receivable (ERPNext requires debit_to currency to match the document currency), and let the Payment Entry settle into the company bank with ERPNext booking the FX gain/loss. Documented the model and ERPNext's one-currency-per-customer constraint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A USD order against an INR test company is booked with a seeded USD->INR rate, a USD receivable debit_to, a converted base total, and a Payment Entry. Verified green against ERPNext 16.25 + Medusa v2.4.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread ecommerce_integrations/medusa/connection.py
Comment thread ecommerce_integrations/medusa/order.py
Comment thread ecommerce_integrations/medusa/order.py
Comment thread ecommerce_integrations/medusa/returns.py
Comment thread ecommerce_integrations/medusa/returns.py
_validate_request logged the raw request bytes on a signature mismatch, which create_medusa_log could not serialise (TypeError -> HTTP 500). Decode the body for the log and raise AuthenticationError so an unsigned/forged webhook gets a clean 401. Found while smoke-testing the live Medusa->ERPNext callback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread ecommerce_integrations/medusa/returns.py
Comment thread ecommerce_integrations/medusa/returns.py
Resolves the five 'confirm against Medusa v2 Admin API' TODOs, verified
against docs.medusajs.com and the v2.4.0 OpenAPI/zod validators:

- variant create: title + prices are required and options is a
  title->value record — always send prices ([] default) and the
  Default option mapping
- product create: v1-style nested type {value} would 400 on the strict
  schema — resolve Item Group to a real Product Type via
  get_or_create_product_type() and send type_id; drop None-valued keys;
  declare the Default option (create-only)
- price selection: skip region/context-scoped prices (price_rules),
  prefer the price-list currency from Medusa Setting, fall back to
  first rule-free price
- confirmed correct as-documented: implicit 'Default option'
  convention and major-unit price amounts (TODOs replaced with
  doc-verified comments)

Adds test_variant_price_selection + test_upload_erpnext_item_payload
and aligns fixtures with real API shapes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread ecommerce_integrations/medusa/returns.py
kiegsgroot and others added 2 commits July 2, 2026 12:16
…bscriber

Turn medusa_subscriber/ into an installable npm package while keeping
copy-in supported (README documents both). Adds:

- package.json: scoped name, CJS build to dist/ with declarations, main
  entry (forwardToErpnext + handler/config re-exports) and "./subscriber"
  subpath for the thin app-side registration shim; peer deps on
  @medusajs/framework and @medusajs/medusa ^2.4.0.
- tsconfig.json: src -> dist, Node16/CJS, declaration: true.
- src/index.ts: main entry re-exporting the forward lib and subscriber.
- .github/workflows/publish-subscriber.yml: FORK-ONLY publish workflow
  (GitHub Packages, version <major.minor>.<commit count>, idempotent
  skip-if-published). Strip this file from the branch used to file the
  upstream PR.

Medusa only discovers subscribers from the app's src/subscribers dir, so
consumers keep a one-line re-export file there (documented in README).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…esolves

The ^2.4.0 peer floated to 2.17.2 in a clean install, whose own peer
(framework@2.17.2) conflicted with the pinned framework 2.4.0 devDep
(ERESOLVE). Pinning medusa 2.4.0 for the build keeps both aligned with
the deployed app.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Try greploops.

kiegsgroot and others added 4 commits July 2, 2026 12:43
…imported

The full medusa package pulled the entire dependency tree into the
package build (npm resolver ground for 25+ min in CI). The subscriber
imports @medusajs/framework only; medusa stays a peerDependency for
consumers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
npm auto-installs the package's own peerDependencies; @medusajs/medusa
^2.4.0 floats to latest whose framework peer conflicts with the pinned
2.4.0 devDep. Build only needs framework types.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…don't match the paths filter)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread ecommerce_integrations/medusa/order.py
Comment thread ecommerce_integrations/medusa/inventory.py
Comment thread ecommerce_integrations/medusa/order.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
}
)

_add_shipping(taxes, order, setting)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Shipping Added Per Taxed Line _add_shipping() runs inside the line-item tax loop. A two-line order with tax on both lines appends the same shipping charge twice, while an order with no taxable lines never appends shipping at all, so the Sales Order and later invoice can overcharge or drop shipping.

Rule Used: What: Ignore the pull request description and eval... (source)

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.

1 participant