Installation
Requirements
Magento 2.4.x — tested on 2.4.9, compatible with previous 2.4.* releases. PHP 8.1–8.5. Requires codingrow/module-core (installed automatically as a Composer dependency) for the shared Codingrow admin menu and license validation.
Setup steps
- Add the credentials you'll receive by email to
auth.jsonin your Magento project root:{ "http-basic": { "repo.codingrow.com": { "username": "...", "password": "..." } } } composer config repositories.codingrow composer https://repo.codingrow.comcomposer require codingrow/module-oetibin/magento module:enable Codingrow_Oetibin/magento setup:upgrade && bin/magento setup:di:compile && bin/magento cache:flush- Paste your license key into Admin → Stores → Configuration → Codingrow → Order Export & Tracking Import → License → License Key, save, then
bin/magento cache:flush. - Under the same section's General group, set Enable module to "Yes" — the module ships enabled at the Magento level but functionally off, so it never exports anything until you explicitly turn it on.
- Create your first profile under Codingrow → Order Export & Tracking Import → Export Profiles — see Profiles below.
The whole profile admin, mapping builder, log and every notification email are available in 7 languages, auto-selected per admin user locale:
Uninstallation
composer remove codingrow/module-oeti then
bin/magento setup:upgrade && bin/magento setup:di:compile && bin/magento cache:flush.
Profiles and the export/tracking log (codingrow_oeti_profile,
codingrow_oeti_log, codingrow_oeti_tracking_log) are never deleted automatically —
use Export profile first if you want to keep a copy
of your profile configuration.
User guide
Profiles
Everything in the module is organized around profiles, listed under Codingrow → Order Export & Tracking Import → Export Profiles. Each profile is completely independent: its own name, its own enabled/disabled switch, its own trigger status(es), its own destination (channel + format), its own field mapping and its own row in the export log — you can run any number of profiles side by side (one per supplier, one per carrier, one for an internal ERP), and disabling or deleting one never affects the others.
A profile that is disabled (Profile enabled = "No") never exports anything, whether triggered automatically, from the manual action, or from the CLI command — this is independent from the module-wide Enable module switch under System Config, which acts as a single master off-switch for every profile at once.
Channels & formats
Every profile picks one channel (where the payload is delivered) and one format (how it's serialized) — the two choices are independent, so any format works with any channel.
| Channel | Typical use | Key settings |
|---|---|---|
| REST API | Sends the payload as an HTTP request to a webservice | Destination URL, HTTP method (GET/POST/PUT/PATCH), authentication (None, HTTP Basic, Bearer token), minimum seconds between requests (rate limiting, 1 second by default) |
| FTP / SFTP | Uploads the payload as a file to a remote server | Host, port (21 by default), username, password, remote path, optional FTPS (explicit TLS), passive mode |
| Local file | Writes the payload to a file under this store's
own pub/media/, for an external system to pick up over HTTP |
Subfolder — the file lands under
pub/media/codingrow_oeti/<subfolder>/ (default subfolder:
"default") |
Formats: JSON, XML or CSV, chosen independently of the channel above — the same field mapping (see below) drives all three, the format only changes how it's serialized.
Field mapping
The Mapping tab is a table of rows, each connecting a target (the field
name in the output — a dot means one level of nesting, e.g. customer.email) to a
source path chosen from a dropdown of every available order/customer/item
field. Empty rows are ignored on save.
Rows are read top to bottom in the same shape as the output itself: an optional Header template first, then the order-level mapping rows, then any Repeat blocks (see below), and an optional Footer template last.
Transformations
Each mapping row can apply one transformation to the source value before it's written to the target field:
| Transform | What it does |
|---|---|
| None | Direct copy of the source path's value (falls back to "Default value" when the source is empty). |
| Static value | Ignores the source entirely, always writes the fixed text in "Value". |
| Strip HTML tags | Removes HTML markup from the source value — useful for a product name or description that carries residual formatting. |
| Text template | Free text with placeholders, e.g.
{order.shipping_address.firstname} {order.shipping_address.lastname}.
A function is also available inside a placeholder:
striphtml{path} (same as the Strip HTML tags transform, usable inline) and
substr{path,N} (drops the first N characters, e.g.
substr{order.some_code,3}). |
| Search and replace | Looks for the "Search" text inside the source value (case-insensitive) and replaces it with "Value" exactly as given. |
| Number | Serializes the value as a real JSON number instead
of a quoted string — use it when the destination validates the field's TYPE strictly
(a quantity sent as the database-raw string "1.0000" gets rejected by
some APIs, while 1.0 as a real number is accepted). This is
not the default for numeric-looking fields on purpose: turning it on
everywhere would silently strip leading zeros from things like a postcode or an article
code, so it's an opt-in choice per row, not an automatic behaviour. |
The same {path} / striphtml{} / substr{} syntax used
in Text template rows is also available in Repeat block rows and in the profile's optional
Header/Footer.
Repeat blocks
A repeat block builds a nested array in the output — the most common case is one entry per order item. Each block has a Block output key (where the array is written in the output) and a source collection (what it repeats over, e.g. the order's items), plus its own set of mapping rows underneath, evaluated once per element with "current" scoped to that element — so a row's source path inside a repeat block refers to fields of the current item, not the order as a whole.
items, source collection
"Order items", and two rows (sku ← current item SKU, qty ←
current item quantity) produces:
{
"items": [
{ "sku": "43241", "qty": 2 }
]
}
A profile can define any number of independent repeat blocks — for example one for the
item list and a separate one for a list of applied discounts.
Trigger conditions
The General tab has a nested AND/OR condition builder — the same tree-style interface as
Magento's own Cart Price Rules (an "Add" link on every group to add a condition or a nested
sub-group, a remove icon on any node) — that decides both which order rows a
profile actually exports and whether the profile exports the order at all.
Each condition compares a product attribute (e.g. sku, a custom "supplier code"
attribute, price...) against a value, with an operator: equal to/not equal to,
contains/does not contain, starts with/ends with, greater than/less than (or equal to), is
empty/is not empty.
Conditions inside a group combine with ALL (AND) or ANY (OR), and groups can nest inside
groups — for example sku contains "ABC" OR (supplier_code = "Supplier2" AND price >
10). No conditions configured = no filter, every row is exported (same as the old
"Only supplier items" = "No" before version 1.3.0).
Typical use with several dropshipping suppliers: one profile per supplier,
each with its own condition (e.g. Profile A: supplier_code = "Supplier1",
Profile B: supplier_code = "Supplier2") — an order containing items from both
suppliers correctly triggers both profiles in parallel, each one exporting only the rows that
belong to it. If no row matches a given profile's conditions, that profile simply does not
export that order (see the "Skipped" log status below) — not an error.
Every attribute used in a condition automatically becomes available as a mappable field in
the payload (items.attributes.<code>, see Field mapping above). When the
conditions exclude rows from an order, the live preview shows an
explicit warning naming how many rows were left out and why — so an empty or shorter-than-
expected payload is never a silent surprise.
Live preview
The Preview button on the profile edit page builds a temporary, never-saved profile from whatever is currently in the form — including changes you haven't saved yet — and runs it against a real order you pick, through the exact same code path used for a genuine export. The result is the literal JSON/XML/CSV payload that order would produce, so you can correct the mapping before ever activating the profile, instead of finding out from a failed real export.
Trigger & deduplication
The Trigger status multiselect on the General tab lists which order status(es) start an automatic export for that profile (Ctrl/Cmd-click to select more than one) — checked only on the automatic, event-driven trigger; the manual grid action and the CLI command ignore it on purpose, since triggering by hand is itself the deliberate choice to export regardless of status. The manual "Export via Codingrow Order Export" action on the Sales > Orders grid runs every enabled profile against the selected orders in one click — each profile's own Trigger conditions decide what it actually sends, exactly like the automatic trigger.
Every attempt is written to that profile's log (see below), and the log is also what prevents duplicates: the same order is never exported twice automatically by the same profile, whatever the order does afterwards (further edits, status changes back and forth). If a genuine resend is needed — after fixing a destination-side issue, for example — use Force resend, which explicitly bypasses this protection.
Tracking import
A per-profile Tracking Import tab (off by default) turns on a periodic poll that asks your supplier which items have shipped and, for those items, creates a native Magento shipment with its tracking number — independently per profile, reusing the profile's own Trigger conditions to know which items of the order belong to it.
| Setting | What it does |
|---|---|
| Reference field | Which already-mapped field holds the supplier's order reference. It is captured automatically on every export attempt — successful or failed — so tracking still works when the order already existed on the supplier's side (for example, entered by hand before the automatic export). No extra send-side configuration. |
| Item matching attribute | Which product attribute identifies an item for the supplier — usually the same one used in the Trigger conditions (e.g. a "supplier code"). Suppliers report their own items by their own code, not the Magento SKU, so the module has to be told which attribute to compare. |
| Verification endpoint | URL, authentication and frequency (in minutes), plus the mapping of the supplier's response: where to read the reference, the tracking number, the tracking URL (if any), the carrier and the list of shipped items. |
Partial shipments. If an order has items from several suppliers (or one supplier ships in more than one wave), each check creates a shipment only for the items actually reported as shipped at that moment, never beyond what is still left to ship — so an order legitimately ending up with several shipments over time is the intended behaviour, not a bug.
Customer email. When the supplier provides a clickable tracking link (a URL, not just a number), the customer receives an email with a "Track your package" button instead of the plain native Magento email (which would show only the number, with no clickable link for a carrier Magento doesn't recognise natively). When the supplier provides no URL, the standard native email is used, unchanged.
No manual action is needed in normal operation: the check runs on its own in the background at the configured frequency (Magento cron must be running, as for any scheduled task).
Log & Force resend
From version 1.4.0 the log is no longer split per profile (a Log tab inside each profile). A single Log page (button on the Export Profiles grid) lists orders — not individual attempts — one per row, paginated, with the latest export status and the latest tracking status side by side: a single glance at an order even when several profiles/suppliers are involved.
Clicking View full history opens that order's complete history, split into two sections — Export log and Tracking log — each listing every attempt/check from every profile involved, with an expandable details row (exact request payload sent, response body received) and, on the export side, a per-row Force resend button.
On the export side an attempt is Success, Error or Skipped. Skipped is not an error: no order item matched that profile's Trigger conditions, so nothing was sent — useful with several profiles/suppliers to see at a glance why a given profile did not export a given order. On the tracking side, a failed check usually just means the supplier hasn't reported anything shipped for those items yet; it is retried automatically on the next poll, with no manual action needed.
A failed or skipped row's Force resend (export side only) re-runs the export for that exact order through that exact profile right now, bypassing the deduplication protection — a confirmation dialog spells this out first, since it's a deliberate override, available both from the main Log page and from the order detail. Tracking has no manual "force check" equivalent: the next scheduled poll simply tries again.
Failure notification emails
Two General-tab settings control failure notifications, independent of the log (which always records every attempt regardless of these settings): Notification recipients (one or more email addresses, comma-separated — leave empty to send nothing) and Send email for (which events trigger a notification).
The email body includes the actual error returned by the destination, not just a generic
HTTP status line: when the response body is parseable JSON with a message field,
that message is appended verbatim (e.g. "Destination returned HTTP 422 - Minimum product
quantity is 1.0"); otherwise the raw response body is included, truncated to 500 characters —
so the real cause is visible straight from the inbox, without opening the log.
Import / Export profile
The Export profile button on a profile's edit page downloads its entire configuration — general settings, destination, mapping rows and repeat blocks — as a single JSON file, with destination credentials deliberately excluded so the file is safe to store, share with support, or commit alongside a deployment. The Import profile button on the Export Profiles grid re-creates a profile from such a file — the typical use is moving a profile from a staging/demo store to production, or keeping a backup of a mapping you're happy with before experimenting further. Credentials always have to be re-entered by hand after an import, since they were never in the file to begin with.
License
One license per domain, entered under Admin → Stores → Configuration → Codingrow → Order Export & Tracking Import → License → License Key. Includes all updates for that domain. Need to move to a different domain (staging → production, or a site migration)? Your first domain change is free and instant from your account page — no waiting, no ticket needed. From the second change onward, a change is allowed once a year. You can also request a refund within 30 days of purchase, on your own, from the same account page.