For the complete documentation index, see llms.txt. This page is also available as Markdown.

Changelog

All notable changes to Orbit Wholesale B2B are documented here.


v1.0.9 — 2026-07-27

New: Draft Orders — let B2B customers order on account

A whole-cart "order on account" flow. Eligible B2B buyers submit their cart for approval without paying; you confirm and send a Shopify draft order with a payment link (or Net-terms invoice).

  • Submit Order for Approval — for eligible customers, the app replaces the cart's Checkout button with a submit-for-approval button (works on Dawn and most free themes; no Shopify Plus needed). The whole cart is captured, no payment is taken, and the cart is cleared on submit

  • Draft Orders inbox — new admin page (Orders → Draft Orders) with status tracking (Pending / Invoiced / Paid / Cancelled); open a request to review items, customer, and payment term, then Confirm & send payment link or Cancel

  • Payment terms — uses Shopify's built-in terms (Due on receipt, Net 15/30/45/60/90). Cascade: per-order override → Customer Group default → shop default → receipt. Set a group's term in Customer Groups → B2B payment term

  • Auto-create on submit — optional setting to skip manual review: the draft order + payment link are created and emailed automatically the moment the buyer submits

  • Emails — admin notified on a new request; customer emailed the payment link on confirm. Editable templates (English + Vietnamese defaults)

  • Multi-language storefront — button label and confirmation strings are translatable

  • Plan — runs on all Shopify plans

Fixed

  • Quote currency — quotes now display in the store's live currency instead of always showing USD, and amounts render with the plain currency symbol (e.g. $240.00 NZD, not NZ$240.00)

  • B2B group tags — the storefront eligibility tag list now re-syncs on every Customer Group save (previously only on a tag rename), so newly added groups take effect immediately

  • Quotes — the seller now gets an email when a customer replies or counter-offers on a quote (previously only an in-app badge)


v1.0.8 — 2026-07-13

Improved: One-form registration layout, translatable Company section, real store name in emails

  • One continuous form — the Company information section no longer sits inside a bordered box; when a customer picks "A company", the extra fields now flow seamlessly with the rest of the form

  • Company section fully translatable — the I'm registering as options, the "Company information" heading, all its field labels, and the country/state picker placeholders are now editable under Form editor → Content Settings → Company section labels, per language (defaults ship in 6 languages). Previously these texts always displayed in English

  • Emails show your store name — customer-facing emails (application approved/rejected, email confirmation, company invites) now sign off with your actual store name instead of your .myshopify.com subdomain. No setup needed


v1.0.7 — 2026-07-08

New: Custom registration fields & file upload

Add your own fields to the B2B registration form, on top of the built-in set.

  • New field types — Number, Radio, Checkbox (single consent), Checkbox group, and Date, alongside Text, Textarea, Dropdown, and File upload

  • + Add element — build custom fields in the form editor, configure per-type settings (list options, number min/max/step, date range, file rules), reorder, and delete

  • File upload → Shopify Files — applicants attach documents (business license, tax certificate, …); files upload straight to Shopify admin → Content → Files, with server-enforced type and size limits, and appear as links in the submission detail

  • Auto-generated IDs — each custom field gets a stable ID from its label (e.g. reseller_license_ab12), frozen after the first save so submissions keep their reference

  • Built-in fields untouched — the standard fields still work on every plan and can't be deleted or retyped

  • Plan — Custom fields require Advanced+; built-in fields remain available on all plans


v1.0.6 — 2026-06-29

New: Catalog Visibility

Hide your prices from shoppers who aren't your B2B customers. One global setting (Pricing → Catalog Visibility).

  • Two modesHide price only (still buyable) or Hide price and block Add-to-cart

  • Audience — show prices to any logged-in customer, or only to specific B2B customer groups; everyone else is gated

  • Product scope — All products, Specific products, Specific collections, Product tags, or Vendors, plus an Exclude list that stays visible to everyone

  • Masked price — gated shoppers see the currency symbol next to a blurred XXX; the real price is never sent to the page, so it can't be revealed by editing CSS. Price ranges ($10 – $20) are fully masked

  • "Unlock wholesale price" button — appears under the price on product pages and collection cards; opens Shopify's (passwordless) sign-in and returns the shopper to the same page. The label is editable + translatable in Settings → Storefront Text → Catalog Visibility (defaults ship in 6 languages)

  • Takes priority over pricing — a gated shopper never sees a Wholesale Discount badge, B2B price, or Volume Pricing table, even when a rule targets "All Customers"; normal pricing returns the moment they qualify

  • Hard checkout backstop — in Hide price and block Add-to-cart mode, a Cart & Checkout Validation Function blocks checkout for an ineligible buyer who force-adds a gated product

  • Surfaces — product pages, collection/listing cards, search results, the Volume Pricing table, and the cart

  • Plan — Advanced+


v1.0.5 — 2026-05-31

New: B2B Customer Portal

The single-page Reorder Portal grows into a full B2B Customer Portal at /apps/orbit-boost-wholesale/portal — a sidebar shell wrapping four sections so wholesale buyers can self-serve orders, quotes, profile, and invoices without leaving the storefront. Server-rendered through the App Proxy; gated by login + plan + B2B eligibility (Customer Group membership). On mobile the sidebar collapses to horizontal tabs.

  • Sidebar shell — every section (My Orders · My Quotes · Profile · Invoices) renders inside one consistent shell with the active item highlighted

  • My Orders (/portal, /portal/orders) — recent orders with one-click reorder; the /portal/reorder?order=<id> preview shows live B2B + volume pricing (what the buyer pays today, not the historical price)

  • My Quotes (/portal/quotes) — the buyer's quote list embedded in the shell. This is now the canonical "all quotes" entry. Standalone /quotes and /quotes/:id stay for email deep links and not-logged-in access

  • Profile (/portal/profile) — view name / email / company / pricing tier; edit phone (flag country-code picker → E.164) and default address (Country select + dependent Province select). Returns the specific Shopify Admin error and preserves the buyer's input on failure; saves the rest of the address even when a province isn't a valid Shopify subdivision (best-effort, with a warning)

  • Invoices (/portal/invoices) — accepted quotes with Download PDF (Net / Prepay terms) or Pay / View (Due-on-receipt receipt)

Improved: Quotes

  • QuoteSettings now applies on the storefront — the formFields toggles (target price, needed-by date, file attachment, PO number, tax ID) and the copy overrides (heading, description, button, success / reject messages) actually drive the storefront modal. The contact-info section is hidden when the customer is logged in

  • Specific, localized errors (English + Vietnamese) with the actual threshold values — auto-reject minimum quantity / floor %, eligibility (must log in / must be approved B2B), and public-mode rate limit now say exactly what failed instead of a generic "Could not save"

  • Persisted request details — PO number, needed-by date, tax / VAT ID, and submitter IP are stored on the quote and shown in the admin Quote detail. Guest submissions allow a null customer ID

  • Admin deep link fixed — the "Open quote in admin" link in admin emails (/quotes/:id) now opens that quote pre-selected in the inbox

Fixed

  • App Proxy JSON responses no longer wrapped in theme HTML — the quotes, reorder-price, and portal JSON endpoints force Content-Type: application/json, so fetch().json() works (Shopify was rendering JSON inside the theme Liquid layout)

  • Reorder preview pricing — the preview now mirrors the checkout discount Function: it evaluates the wholesale and volume pools, resolves the customer-group tier, and combines them per the shop's discount strategy (stack vs best). Changing a line quantity re-prices via the API so volume tiers show before checkout

  • Quote settings toggles were unlabeled — the admin switches now render their label + helper text correctly

  • "Create group" / quote-settings modals open reliably from the page title bar


v1.0.4 — 2026-05-23

New: Reorder Portal

Customer-facing portal at /apps/orbit-boost-wholesale/portal/orders where eligible B2B customers see their recent orders and one-click reorder selected items back into the cart. Pairs with a scheduled reminder email that nudges customers to come back N days after fulfillment.

  • Recent orders list at /apps/orbit-boost-wholesale/portal/orders — last 20 orders with B2B badge, B2B only filter toggle, Reorder button per row

  • Reorder preview at /apps/orbit-boost-wholesale/portal/reorder?order=<id> — preview each item, adjust qty, deselect out-of-stock items, Add selected to cart

  • Wholesale-aware pricing in the preview — the price each item shows is what the customer would pay today (their current customer group + active rules + product eligibility), not the historical order price. Cart total matches the preview total

  • Scheduled reminder cron — up to 3 reminders at configurable delays (e.g. 30 / 60 / 90 days after fulfillment). Daily at 06:00 UTC, multi-server safe via Mongo lock. 3-day query window absorbs missed ticks

  • Reorder reminder email template under Settings → Email Templates → Reorder Reminder, with shortcodes {{first_name}}, {{shop_name}}, {{days_ago}}, {{reorder_link}}, {{unsubscribe_link}}. Per-locale translations (en, vi, fr, de, es, ja)

  • HMAC-signed unsubscribe link in every email — customer clicks → recorded in ReorderUnsubscribe → all future cron runs skip them

  • CustomerGroup-derived eligibility — a customer is B2B-eligible iff they belong to any Customer Group. No tag list to configure. Eligibility used everywhere: portal access, order tagging, reminder cron targeting

  • orders/create webhook auto-tags B2B orders with orb2b-b2b. Idempotent. Reminder cron queries by this tag

  • Schedule + sender config under Apps → Orders → Reorder Settings → Schedule (enable, day delays, marketing consent). Email content lives in Email Templates so it's edited alongside Registration + Quote emails

New: B2B Prices app embed

Replaces the drag-into-section Wholesale Price + Card Prices blocks with a single app embed (target: head) that overrides retail prices everywhere on the storefront — PDP, sticky add-to-cart, and product cards on collection / search / homepage surfaces.

  • One toggle, applies everywhere — Theme editor → App embeds → Orbit B2B Wholesale → ON. No per-section drag, no theme code edits, no separate blocks for PDP vs cards

  • Two display modes — Strikethrough (default: ~$25.00~ $21.25 [Save 15%]) or Replace ($21.25). Pick under Settings → General → B2B Price Display

  • Save badge computed from total savings — works correctly when Wholesale + Volume stack. (retail - final) / retail formula so a 30% wholesale + 5% volume stack shows Save 34%, not the nominal value of any single rule

  • Optional currency code suffix ($21.25 USD) toggleable under the same settings section

  • Theme adapter chain — built-in adapters for Dawn, Horizon, Craft, Refresh + a generic fallback covering most other themes. Each adapter declares the right CSS selectors for PDP price, sticky bar, card selector, card link, card price container, card price text

  • Merchant selector overrides — the embed's advanced section in Theme editor (or the theme_override shop metafield for operators) lets you point at custom selectors if your theme is exotic. Block settings take precedence; metafield is the operator escape hatch

  • Variant-change reactivity without flash — uses the cart Add to cart button's disabled attribute as the theme render lifecycle signal. Pre-hide CSS gate re-engages on enabled → disabled, releases on disabled → enabled. Eliminates flash of retail price during Section Rendering API swaps

  • Quantity-change reactivity — Volume Pricing tiers update live as the customer changes the qty input or presses +/- stepper buttons. The PDP shows exactly what the customer will pay at checkout for that qty

  • Pre-hide layerorb2b-pending class on <html> hides theme price selectors via visibility: hidden until the resolver decides what to do. 1.5s safety release prevents permanent freeze

  • Settings → General → B2B Price Display is the merchant entry point: pick mode + toggle Save badge + toggle currency code

Removed

  • wholesale-price.liquid section block (drag-into-PDP) — replaced by the B2B Prices app embed

  • wholesale-card-prices.liquid body embed — folded into the new app embed's payload + listing JS

  • extensions/wholesale-block/src/main.jsx, pricing.js, PriceDisplay.jsx, vite.config.js — the wholesale-price build pipeline (volume-pricing-table build kept)

  • Per-feature From name + Reply-to fields in Reorder Settings → Schedule — reorder reminders now use the shared SMTP From email (same as registration + quote emails)

  • eligibilityTags text input in Reorder Settings — replaced by automatic eligibility derivation from CustomerGroup membership

Improved: Email Templates

Reorder reminder is the third feature with editable Email Templates (alongside Registration and Quote). The Reorder Reminder template lives under Settings → Email Templates → Email Templates · Reorder, edited the same way as the others.

The cron job uses EmailTemplateService.getForSending and the shared EmailService.send (shop's SMTP config) instead of a feature-specific renderer. One sender path, one template editor, one set of shortcodes.

Improved: Wholesale rule "winning" logic clarified in Help Center

Existing "Which rule wins when multiple match" docs updated to reflect the actual specificity → best-price → priority tiebreak chain in pricing.js. Old docs claimed priority is the primary factor (it's actually the final tiebreaker). Updated example calls out the gotcha that caused several support tickets: a logged_in rule with 0% discount silently outranks an all rule with 15% discount because logged_in scores higher on specificity.

Improved: Settings → General → B2B Price Display section

New subsection on the existing General settings page. Display mode (strikethrough / replace) + Save badge toggle + Currency code toggle. Synced to shop.metafields.orbit_wholesale.price_display via DiscountSyncService so the storefront embed reads it without a per-request DB hit.

Improved: PLAN_ENFORCEMENT=off (local dev) also writes platinum metafield

Setting PLAN_ENFORCEMENT=off previously unlocked admin UI + proxy gates but storefront Liquid blocks (which read shop.metafields.orb2b.plan) still saw the shop as Free. The boot-time hook now iterates active merchants and writes platinum to each shop's plan metafield when env=off, so storefront blocks render unlocked on local dev without per-shop unlock-store curls.

Fixed

  • Pre-hide CSS gate properly re-engages on variant change so retail price doesn't flash during theme's async section render

  • Wholesale rule resolver no longer returns the LAST rule applied as "winning" in stack mode — Save badge text now reflects total savings ((retail - final) / retail) not the last rule's discountValue. Eliminates "Save 0%" displaying $7 off

  • Cart overlay markup wipes the theme's existing price text before injecting the B2B overlay — eliminates the triple-render of retail + struck-retail + B2B in some themes

  • Collection card overlay same fix — was duplicating the theme's $600.00 USD text alongside the overlay

  • Customer.acceptsMarketing deprecated GraphQL field swapped to emailMarketingConsent.marketingState (removed in API 2024-01+; would have silently broken reorder reminders on 2025-07 API)

New: Quote requests (RFQ)

A full B2B price-negotiation workflow inspired by Alibaba's "Contact Supplier" flow. Buyers click Request a Quote on any product page, your team replies with custom prices, buyer accepts → Shopify draft order created. Works on every Shopify plan, no Plus required.

  • Storefront block — Drag the Request a Quote app block onto product pages from theme editor. The modal opens inline (no redirect) with the product's variants pre-loaded. Logged-in customer info auto-fills name + email via Liquid.

  • Buyer flow — Set quantities per variant, suggest a target price (optional), add a message, attach a PDF / JPG / PNG up to 2 MB.

  • Admin inbox — Email-style inbox at Apps → Orbit B2B → Quotes with filter tabs (Pending / Replied / Negotiating / Accepted / Rejected / Expired), search, unread badges, and background polling every 30s.

  • Customer info card — Each quote shows the buyer's company link, pricing tier link, Shopify tags, and full quote history (total / accepted / rejected / expired + accept rate %).

  • Items + adjustments editor — Edit unit prices, add discount (fixed / percentage), shipping, validity (3 / 7 / 14 / 30 days or custom), payment terms.

  • Conversation thread — Back-and-forth messaging between buyer and admin. Two reply buttons: Send message only (text reply, no price change) vs Send price update (saves a new revision + emails the buyer).

  • Customer storefront page — Buyers see their quotes at /apps/orbit-boost-wholesale/quotes and individual detail pages with Accept / Decline / Send reply buttons (appear once admin has replied).

  • Eligibility rules — Approved B2B only (default — requires b2b / wholesale / tier-* tag), any logged-in customer, or public. Backend re-validates at submit time so frontend tags can't be spoofed.

  • Auto-reject — Floor percentage below catalog (reject low-ball offers) and minimum total quantity (filter retail-size requests). Rate limit per IP for public mode.

New: Payment terms with PDF invoice (any Shopify plan)

Quotes support Due on receipt, Net 15 / Net 30 / Net 60, Prepay 50%, and Custom payment terms — without requiring Shopify Plus's B2B feature set.

  • Due on receipt — buyer accepts → redirected to Shopify checkout invoice → pays immediately

  • Net X / Prepay / Custom — app generates a PDF invoice from the quote, emails it to the buyer with payment instructions. Buyer pays offline (bank transfer / wire / check). Admin marks the order paid manually in Shopify Orders when the money lands.

  • Shopify draft order tags — Every accepted quote creates a draft order tagged with Quote #<number> and a Payment terms: Net 30 custom attribute so admin can trace it from Shopify Orders.

  • orders/paid webhook — Once admin marks paid, the order links back to the quote via webhook for full traceability.

New: Auto-expiry + reminders

A Mongo-locked hourly cron job:

  • Expires any Replied / Negotiating quote past its validity.validUntil → status flips to Expired and buyer receives a polite expired email

  • Sends an Expiring soon reminder email 24 hours before validity ends (one-shot, idempotent)

  • Skips quotes with no validity set (e.g. still Pending)

New: 6 quote email templates

Editable per language under Email Templates → Quote:

  • Quote Submitted (to admin) when a buyer submits a new request

  • Quote Reply (to buyer) when admin sends a price update

  • Quote Accepted (to admin) when buyer accepts the offer

  • Quote Invoice (Net terms) (to buyer, with PDF attached) for Net X / Prepay / Custom payment flow

  • Quote Expiring Soon (to buyer) 24h before validity

  • Quote Expired (to buyer) when validity lapses

All templates ship with default translations for English, Vietnamese, French, German, Spanish, Japanese.

New: Multi-language storefront text

Every visible string on the Quote modal + customer detail page is editable per language under Settings → Storefront Text in 6 groups: Modal form, Detail page, Status badges, Errors & notices, Payment terms, plus the existing Volume Pricing / Register Form / Global groups.

  • Pushed to shop.metafields.orbit_wholesale.app_settings_i18n on save

  • Storefront block reads it inline via Liquid — no extra round trip on cold load

  • Default translations for 6 languages, unlimited additional languages via the language tabs

Improved: customers/delete webhook (GDPR)

When a Shopify customer is deleted, the app now also deletes their Quotes + QuoteMessages alongside the existing Company / CustomerGroup cleanup. Idempotent + safe to retry.

Improved: Documentation

  • New Quotes section in the in-app Help Center (Documentation menu) — overview, modal flow, admin inbox, payment terms, auto-expiry, email templates

  • Workflow diagrams updated with 5 flows: Buyer requests → Admin replies → Accept (Due on receipt) → Accept (Net X PDF) → Auto-expiry

  • New troubleshooting guides for common Quote issues

Fixed

  • Modal "Request a Quote" no longer redirects to App Proxy URL — opens inline on product page

  • Liquid block now uses Shopify CDN via asset_url filter for quote-storefront.{js,css} (same pattern as variant-drawer) — faster + resilient to backend outages

  • Quote thread message authors now show Wholesale Team / Customer / System correctly based on author.type instead of falling back to "Customer" for everyone

  • "Send price update" admin button no longer triggers loading spinner on both reply buttons — each tracks its own action


v1.0.3 — 2026-05-15

New: Quick Order

Bulk-order form for B2B buyers, embeddable on any page or as a dedicated /pages/quick-order.

  • Multi-form — Create different forms for different customer groups (Gold, Silver, Wholesale, etc.). Storefront automatically picks the most specific active form for the logged-in customer

  • 4 product modes — All products, Specific products, Specific collections, or Products with tags. Use the Shopify resource picker to select

  • Auto-populate — In Specific products mode, the form pre-fills rows with the chosen products on page load. Multi-variant products show as parent group with chevron toggle to expand/collapse variants

  • MOQ rules — Min, max, and step quantity per row. Industry presets (Beverage, Apparel, Cosmetics, Electronics, Food) one-click apply suggested values

  • Quick-pick chips — Configurable shortcuts under each row (e.g. 12 / 24 / 48 / 96). Click to fill the focused row or bulk-fill all available rows

  • Pagination & infinite scrollNone / Pagination (Prev/Next + page number) / Infinite scroll (Load more button). Items per page configurable. Background prefetch loads remaining chunks while the user is busy with the first batch — pagination shows the correct total page count from first paint

  • Skeleton loader — Shimmer-animated placeholder while the form initializes; respects prefers-reduced-motion

  • Full design tokens — Primary color, button text color, text/subdued/border/background/row-hover, font size, row padding, border radius, custom CSS

  • Per-locale translations — Heading, description, button labels translatable per published locale. Generic strings (In stock / Out of stock / Quick pick / errors) come from bundled translations (en, vi, fr, de, es, ja)

  • Tier pricing in Unit column — Wholesale Discount applied per variant for the logged-in customer (matches the price shown on product pages). Volume Pricing tiers apply at checkout via the Shopify Function

New: Metafield-first storefront pattern

All storefront blocks (Wholesale Price, Variant Drawer, Quick Order) now read directly from Shopify metafields in Liquid — zero app proxy round trip on cold load. Eligibility, locale, and config resolve client-side using the customer tags Liquid hands down. App proxy is used only for customer-aware work (per-variant tier pricing).

New: Tax hub (preview)

New top-level menu Tax with four coming-soon sub-pages: Tax Rules, Tax-exempt customers, Invoices, Settings. UI placeholder for the upcoming VAT / invoice feature.

Improved: Hub URL consistency

All hub landing pages now use the -hub suffix: /pricing-hub, /customers-hub, /orders-hub, /tax-hub, /settings-hub. Feature pages keep their short URLs (/wholesale-pricing, /companies, /orders, /settings). Existing breadcrumb links updated.

Improved: Quick Order admin polish

  • Settings consolidated into the admin form (drop show_header / show_image / max_visible_rows from theme block schema — they live on the form now and apply across all block instances)

  • Locale-aware Copy section with per-locale fallback to primary

  • Translation index lists Quick Order forms with missing-keys count + restore default

  • List page mirrors Wholesale Discount pattern: status tabs, search, sort popover, bulk delete, pagination, inline status switch

  • Collection list uses local state + immediate picker result (no waiting for GraphQL refetch)


v1.0.2 — 2026-05-13

New: Companies (B2B organizations)

A dedicated record for each B2B business buyer. Each Company can have multiple contacts (the people who place orders for it) and an assigned Customer Group (pricing tier). When you set a tier on a Company, every contact in that company automatically gets the tier's tag and the wholesale pricing that goes with it.

  • 5 tabs in the Company editor — General, Customer group, Address, Registration (shareable invite link), Contacts (B2B Customer Picker UI for add/remove)

  • One-click invite links — Each Company gets a unique registration URL. Share it with the company's team so they can self-register against that company in one click

  • Invite by email — Send the invite link to one or many addresses with a single click; recipients get the templated invite email

  • Tier inheritance — Assign a Customer Group to a Company → all contacts inherit the tier's tag automatically. Remove a contact → tag removed (unless they're a contact of another company with the same tier)

  • Active / Inactive status — Inactive companies hold contacts dormant: their tier is intentionally NOT applied until you flip the status back to active

New: B2B Customers — unified view

The Registration page now has two views you switch between with a button at the top:

  • Applications — the inbox of sign-up requests waiting for review (the old default view)

  • All customers — aggregated list of every active B2B customer regardless of how they were added (form approval, Customer Group, or Company contact)

Filters on the All customers view:

  • Buyer-type buttons: All / Individual buyers (solo wholesale — not in any company) / Company contacts

  • Tier dropdown next to search: pick All tiers (default), a specific Customer Group, or "No tier yet" to surface customers you still need to assign a discount tier to

  • Empty state with quick links to Customer Groups and Form Settings when no customers exist yet

New: Three registration modes on the storefront form

The storefront registration form now supports three modes via a toggle:

  • Individual buyer — solo wholesale customer, fills personal info + tax ID + address inline in the main form

  • A company — registers as a new company. A "Company information" section appears with company name + tax ID + HQ address. On approval, the app auto-creates a Company record + adds the submitter as its first contact

  • Join company — when the form is opened via a company's invite link (?company=<slug>), the toggle is hidden and the customer registers as a contact of that company (only personal info asked; the linked company already has the rest)

Mode toggle labels (Individual buyer / A company / Company information heading) are translatable via Settings → Translations and ship with defaults for English, Vietnamese, French, German, Spanish, and Japanese.

New: Company registration approved email

A dedicated template (company_registration_approved) sent when a "new-company" mode submission is approved. Welcomes the contact and includes the company's invite link so they can share it with their team. Ships with translations for the 6 supported locales out of the box.

New: Help Center inside the app

The Dashboard's "Documents" button now opens an in-app Help Center (/help-center) with a sidebar of topics mirroring this docs site. No more leaving the admin to read the manual.

Improved: Layout for editor pages

All New / Edit pages for Wholesale Pricing, Volume Pricing, and Companies now use a 3-column grid (300px left tab nav | content | 300px right info sidebar) with consistent widths and Polaris inlineSize="large". Companies info card now renders properly as a card section.

Improved: Rich text editor loading state

The email body editor shows a spinner placeholder while templates are loading from the server, instead of mounting an empty Quill instance that flashes blank.

Operational: Scheduled jobs

Two safety-net jobs now run automatically in the backend:

  • Reconcile Company ↔ CustomerGroup membership — daily at 03:00 UTC. Backfills CustomerGroup.customerIds if any drift slips past the webhook path

  • Cleanup orphan customer IDs — weekly Sunday 04:00 UTC. Removes Shopify customer GIDs from Company contacts and Customer Group members when the customer no longer exists on Shopify (covers missed customers/delete webhooks)

Both jobs use a Mongo lock so multi-instance deploys don't double-run. Set DISABLE_SCHEDULED_JOBS=1 in development to skip.


v1.0.1 — 2026-05-09

New: Variant Drawer

A brand-new feature that lets B2B customers order multiple variants at once through a slide-out drawer.

  • Multi-variant ordering — Customers select quantities for every variant (size, color, style) in a single drawer and add them all to cart at once

  • Grouped variant display — Products with 2 options (e.g. Color + Size) show the first option as clickable swatches that filter the variant list below

  • 4 display modes — Open on variant swatch click, secondary button next to Add to Cart, replace Add to Cart, or floating sticky button

  • Order constraints — Set minimum order quantity per variant, minimum total across all variants, and maximum per variant. Real-time validation in the drawer

  • Action buttons — Configurable Add to Cart (primary) and Start Order (secondary) buttons. Each can be enabled/disabled per rule

  • 25+ design tokens — Full control over every visual element: drawer background, header, swatches, variant rows, quantity stepper, footer, action buttons, MOQ notice, trigger button, and custom CSS

  • Zero-latency styling — Design settings stored in Shopify metafields and read in Liquid at page render. Drawer opens with correct styling instantly, no style jump

  • Per-locale labels — Drawer title, button labels, and action text can be translated for each published store language. Fallback to primary locale for untranslated fields

  • Live admin preview — Full-page editor with 3-panel layout (section nav, live preview, settings). Preview uses real product data from your store

  • Real product picker — Click the product link in the editor sidebar to pick any product from your catalog. Variants and options update dynamically in the preview

  • Customer/product/market targeting — Same powerful eligibility system as Wholesale Pricing: target by customer tags, groups, login status, specific products, collections, product tags, or Shopify Markets

  • Exclude rules — Exclude specific customers, products, or collections even if they match the eligibility criteria

  • Priority system — When multiple rules match the same product, the highest priority rule wins

Improved: Save bar across all pages

  • All admin pages now use Shopify's native <ui-save-bar> component with proper loading state

  • Save button shows a spinner while saving; Discard button is disabled during save

  • Applied to: Wholesale Pricing (New/Edit), Volume Pricing (New/Edit), Customer Groups (Detail), Settings

  • Error feedback via shopify.toast.show() with error details

Improved: Settings — About section

  • Updated app description and version number

  • Added feature list reflecting all current capabilities


v1.0.0 — 2026-04-01

Initial release

  • Wholesale Pricing — Create rules to set special prices for B2B customers by percentage off, fixed amount off, or fixed price. Target by customer tag, login status, specific products, collections, product tags, or Shopify Markets. Per-variant pricing support. Rule priority and scheduling with start/end dates

  • Volume Pricing — Quantity-break and amount-break tier discounts. Three discount methods: same variant, mix variants, mix products. Storefront pricing table with multiple templates (Default, Alibaba Cards). Table design customization

  • Customer Groups — Organize B2B buyers into tiers (Bronze, Silver, Gold, Diamond) with color-coded tags. Bulk assignment via registration approval

  • B2B Registration Form — Drag-and-drop form builder with 9 field types. Custom design settings. Approval/rejection workflow with resubmission support. Auto-approve option

  • Email Notifications — 4 email templates (new submission, approved, rejected, auto-approved). Rich text editor with shortcodes. Custom SMTP support. Built-in relay fallback

  • Storefront Translations — Translate pricing labels, volume table headers, registration form fields, and email templates into any published store language

  • Card Prices — Automatic wholesale price display on collection pages and product cards

  • Discount Strategy — Choose between "best price for customer" (default) or "stack" mode for combining wholesale + volume discounts

  • Shopify Functions — Checkout discounts applied via Shopify's native discount function — no coupon codes needed

Last updated

Was this helpful?